본문 바로가기
STUDY

수료증 만들기를 python-pptx 를 이용해서 자동화해보자.

by PsychoFLOOD 2024. 12. 14.
728x90

최근 작성했던 python-pptx와 python-docx 및 openpyxl 을 이용한 오피스파일 속성 자동수정하기에 이어서 이번에는 python-pptx 패키지를 이용하여 간단하게 수료증 발급을 pptx 및 pdf 파일까지 자동으로 만드는 코드를 만들어보자.

https://nooneelseme.tistory.com/262

 

Python으로 Office file property 변경 및 텍스트 찾기 바꾸기 일괄 자동수행.

얼마 전에 아래와 같이 오피스 파일들의 Creator와 Last Modified by 두가지 속성을 일괄로 수정해주는 파이썬 코드를 작성해보았다.https://nooneelseme.tistory.com/248 Python으로 Pptx Docx Xlsx 모두 속성 변경하

nooneelseme.tistory.com

우선 수료증 발급에 필요한 pptx 파일의 백그라운드 이미지로 사용할 적당한 이미지를 하나 만들어둔다.(background.png)
(구글에서 검색해도 아래와 같이 쓸만한 이미지들이 많이 있다.)

https://www.google.co.kr/search?sca_esv=dfa970682d15eccc&sxsrf=ADLYWIKxH6-u6TRHEeFl5gaDNGtOAABRhQ:1734151801323&q=certificate+background&udm=2&fbs=AEQNm0DmKhoYsBCHazhZSCWuALW8l8eUs1i3TeMYPF4tXSfZ9zKNKSjpwusJM2dYWg4btGIloDPbQR-oiJ32NzmgLaxPZcHtTzj69ncvIXKolD9RdCsDfMjwGtNY7xi5e1TgCjxBjjZJzMV2qSGQpFWmUwojJMsHtxTI2_d9MeRVZutXMtu5PU7AyeR7UNgCmg-A8-cBkNpW&sa=X&ved=2ahUKEwjdt-rwuqaKAxXOs1YBHey3B0MQtKgLegQIDRAB&biw=1920&bih=1067&dpr=1.5

 

certificate background - Google 검색

126,100+ Certificate... www.istockphoto.com

www.google.co.kr

컨셉은 tkinter를 이용해서 text 입력창에 아래와 같은 형식으로 텍스트를 만들어두면 해당 텍스트를 파싱해서 각각의 사람의 정보에 해당하는 수료증을 자동으로 만드는 컨셉이다.

NAME|Company|OrganizationName|Date|Courses(1~7)

이름과 회사 그리고 조직명 교육을 이수한 날짜 그리고 어떤 코스를 수강했는지에 대한 정보이다. 코스는 나의 경우는 7가지가 있었는데 이는 적당히 소스에서 조정하면 된다.(course_list 리스트의 값을 조정)

코드의 예제를 보면 알겠지만 아래와 같은 식으로 데이터를 입력시 | 를 구분자로 하여 스트링을 나눈후 적당한 위치에 loop를 돌면서 스트링을 넣어주는 식이다.

홍길동|좋은회사|나쁜부서|2024.12.10 ~ 2024.12.15|1234567

그리고 마지막의 코스 데이터는 사람에 따라 1부터7까지 모두 들을수도 있고 2부터 5까지 들을수도 있고 등등 경우의 수가 많으므로 코스숫자를 쭉 넣어두면 하나씩 뜯어와서 코스데이터의 리스트에 해당하는 인덱스의 스트링을 들고와서 넣어주도록 하였다.

마지막으로 체크박스를 체크하면 PDF 파일까지 생성하게 되는데 comtypes 패키지를 이용하였다.(다른 방법은 딱히 찾아보지 않았다. 대량의 파일 생성시 power point application이 계속 뜨고 사라지면서 pdf 파일이 생성되는데 이 과정이 느려서 따로 체크박스로 빼두었다.)

오늘의 예제는 적당한 백그라운드 이미지를 만든 뒤 이를 이용해서 pptx파일을 만들도록 하였지만 템플릿이 되는 ppt를 미리 만들어두고 이를 복사해서 수정하는 식으로 구성하는 것도 더 좋을 듯도 싶다..

수행이미지
만들어진 ppt 결과물 (백그라운드 이미지를 적당히 넣었더니 스트링이 이미지와 겹쳤다..ㅎㅎ..)

 

원하는 대로 파일이 잘 생성되었다.

소스는 아래와 같다.

import sys
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.filedialog as fd
from tkinter import *
from tkinter import messagebox
from unicodedata import normalize
import os
import traceback
import clipboard


from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.enum.shapes import PP_PLACEHOLDER

import comtypes.client

current_count =0
offset=8


if getattr(sys, 'frozen', False):
    execute_path = os.path.dirname(os.path.abspath(sys.argv[0]))
    print("Executed from pyinstaller package...")
else:
    execute_path = os.path.dirname(os.path.abspath(__file__))  #.py 로 실행한 경우의 경로
    print("Executed from .py source...")

try:
    os.chdir(sys._MEIPASS)
    print("sys._MEIPASS" + sys._MEIPASS)
except:
    os.chdir(os.getcwd())

print("sys.argv[0] : " + sys.argv[0])
print("Execution Path : " + execute_path)
print("os.getcwd() : " + os.getcwd())


root = tk.Tk()
root.geometry('650x650')
root.title('Certificate maker by NoOneElseME')
root.resizable(False, False)

path = os.path.join(os.path.dirname(__file__), 'icon.ico')
if os.path.isfile(path):
    root.iconbitmap(path)

frame = tk.Frame(root)
frame.pack()

txt = Label(root, pady=offset, text="Certificate Infomations 의 첫번째 텍스트박스에 NAME|Company|OrganizationName|Date|Courses(1~7) 의 형식으로\n 수료자 명단을 넣고 두번째 텍스트박스 Certificate Serial Format에는 연변포맷 시작형식을 넣고 아래 버튼 클릭.\n\n주의사항 : 수료자명단작성시 구분자인 | 앞뒤로는 공백이 없도록 작성.  \nPDF 생성 체크박스는 정상적으로 pptx가 잘 만들어진 것을 확인후 체크하여 재실행요망(PDF 생성시 속도 느려짐)\n")
txt.pack()

btn2 = Button(root, width=100, pady=offset, text="Start Certificate Make!", font="Tahoma 16", foreground="Red")
btn2.pack()

# labelframe and input entry
frame2 = LabelFrame(root, text="Certificate Infomations", padx=5, pady=5, height = 410, width = 640)
frame2.place(x=5, y=220)

txt2 = Label(frame2, text="NAME|Company|OrganizationName|Date|Courses(1~7)")
txt2.place(x=10, y=5)
input1 = Text(frame2, width=55)
input1.place(x=10, y=25, width=600, height = 280)
input1.insert(END, "선우용녀|겁나좋은회사|겁나우울한부서|2024.12.10 ~ 2024.12.15|1234567\n\
살살이|겁나좋은회사|겁나바쁜부서|2024.12.10 ~ 2024.12.15|1234567\n\
멍충이|겁나좋은회사|겁나거지같은부서|2024.12.10 ~ 2024.12.15|1234567\n\
섭섭이|겁나좋은회사|그냥부서|2024.12.10 ~ 2024.12.15|1234567\n\
개똥이|겁나좋은회사|겁나나쁜부서|2024.12.10 ~ 2024.12.15|1234567\n\
홍길동|좋은회사|나쁜부서|2024.12.10 ~ 2024.12.15|1234567\n"\
)

txt3 = Label(frame2, text="Certificate Serial Format")
txt3.place(x=10, y=310)
input2 = Entry(frame2, width=55)
input2.place(x=10, y=330, width=600, height = 20)
input2.insert(0, "XX-XX-24-12-")

mc_check = IntVar()
c1=Checkbutton(frame2,text="Make PDF file.(체크시 PDF file도 같이 생성됨, 단 속도 느림)",variable=mc_check)
c1.place(x= 10, y= 355)
#c1.toggle()



#progress bar
pb_var = DoubleVar()
pb = ttk.Progressbar(root, maximum=100, length=645, variable=pb_var, mode="determinate")
pb.pack(ipady=15)


# certificate make function
def certificate_make():
    global recursive_level
    global all_file_count
    global pb
    global current_count
    global input1, input2
    
    course_list = [
    "Course 1",
    "Course 2",
    "Course 3",
    "Course 4",
    "Course 5",
    "Course 6",
    "Course 7"
    ]
    
    current_count =0
    
    certi_person = input1.get(1.0, "end")
    certi_person_list = certi_person.split('\n')
    print(certi_person_list)
    serial = input2.get()
                    
    for person in certi_person_list:
        current_count+=1
        pb_var.set(current_count/(len(certi_person_list)-1) * 100)
        pb.update()

        prs = Presentation()
        prs.slide_width = Inches(11.69)
        prs.slide_height = Inches(8.27)
        blank_slide_layout = prs.slide_layouts[6]
        slide = prs.slides.add_slide(blank_slide_layout)
        slide_width = prs.slide_width
        slide_height = prs.slide_height
        pic = slide.shapes.add_picture('./background.png', 0,0, slide_width, slide_height)

        textbox = slide.shapes.add_textbox( Inches(0.85), Inches(0.9), Inches(10), Inches(3))
        text_frame = textbox.text_frame
        p = text_frame.add_paragraph()
        p.text = "Certificate - XXXX Training"
        
        font = p.runs[0].font
        font.name = '맑은 고딕 (본문)'  
        font.size = Pt(32)
        font.bold = True     
        font.italic = False  
        font.color.rgb = RGBColor(0, 0, 0)
        #font.spacing = -3  
        font.spacing_mode = 1  # MSO_SPACING_MODE.PERCENTAGE
        font.spacing = 0.6  # 120%
        p.alignment = PP_ALIGN.CENTER

#"NAME|Company|OrganizationName|Date|Courses(1~7)"
        person_info = person.split('|')
        if len(person_info) < 5:
            break
        textbox2 = slide.shapes.add_textbox( Inches(0.7), Inches(1.6), Inches(10), Inches(4))
        text_frame2 = textbox2.text_frame
        p2 = text_frame2.add_paragraph()
        middle_text = "Application area : \nCertificate Owner : "
        middle_text = middle_text + person_info[1] + ' | ' +  person_info[2] + ' | \n                          ' + person_info[0] + '\n'
        middle_text = middle_text + "Course Provider : COMPANY\nTraining Contents :\n"
        p2.text = middle_text
        p2.line_spacing = 1.3
        
        for i in range(0, len(p2.runs)):
            font = p2.runs[i].font
            font.name = '맑은 고딕 (본문)'  
            font.size = Pt(18)
            font.bold = True     
            font.italic = False  
            font.color.rgb = RGBColor(0, 0, 0)
            
        textbox3 = slide.shapes.add_textbox( Inches(1), Inches(3.5), Inches(10), Inches(4))
        text_frame3 = textbox3.text_frame
        text_frame3.word_wrap = True
        p3 = text_frame3.add_paragraph()
        course_info = person_info[4]
        print(course_info)
        course_text  = ""
        for i in range(0, len(course_info)):
            course_text = course_text + str(i+1) + '. '+ course_list[int(course_info[i])-1]+'\n'
            print(course_text)
        p3.text = course_text
        p3.line_spacing = 1.3
        
        for i in range(0, len(p3.runs)):
            font = p3.runs[i].font
            font.name = '맑은 고딕 (본문)'  
            font.size = Pt(14)
            font.bold = True     
            font.italic = False  
            font.color.rgb = RGBColor(0, 0, 0)

        textbox4 = slide.shapes.add_textbox( Inches(1.72), Inches(6.3), Inches(5), Inches(1))
        text_frame4 = textbox4.text_frame
        text_frame4.word_wrap = True
        p4 = text_frame4.add_paragraph()
        p4.text = person_info[3]
        
        for i in range(0, len(p4.runs)):
            font = p4.runs[i].font
            font.name = '맑은 고딕 (본문)'  
            font.size = Pt(18)
            font.bold = True     
            font.italic = False  
            font.color.rgb = RGBColor(0, 0, 0)

        textbox5 = slide.shapes.add_textbox( Inches(9.1), Inches(0.4), Inches(3), Inches(1))
        text_frame5 = textbox5.text_frame
        text_frame5.word_wrap = True
        p5 = text_frame5.add_paragraph()
        p5.text = serial + format(current_count, "03")
        
        for i in range(0, len(p5.runs)):
            font = p5.runs[i].font
            font.name = '맑은 고딕 (본문)'  
            font.size = Pt(14)
            font.bold = True     
            font.italic = False  
            font.color.rgb = RGBColor(0, 0, 0)

        save_file = execute_path+'\\' + format(current_count, "03") + '. ' + person_info[0]+'.pptx'
        save_file2 = execute_path+'\\' + format(current_count, "03") + '. ' + person_info[0]+'.pdf'
        print("Saving pptx file : " + save_file)
        prs.save(save_file)
        
        if mc_check.get() == 1 :
            powerpoint = comtypes.client.CreateObject("Powerpoint.Application")
            powerpoint.Visible = True
            slides = powerpoint.Presentations.Open(save_file)        
            slides.SaveAs(save_file2, FileFormat=32)
            slides.Close()
            print("Saving pdf file : " + save_file)
                        
                        
    messagebox.showinfo("Info", "Processing Complete!")

btn2.config(command=certificate_make)
root.mainloop()

 

728x90

댓글