AI 탐구노트

Python을 이용한 파워포인트 다루기 본문

DIY 테스트

Python을 이용한 파워포인트 다루기

42morrow 2024. 9. 24. 12:48

 

파워포인트 파일을 Python을 이용해 편집해야 하는 필요가 있어 방법을 찾아봤습니다.

 

유튜브나 블로그 글 가운데 관련 글이 많이 있더군요. 

최근 내용을 보면 Microsoft에서 Office365와 함께 Copilot을 내놓으면서 그 기능을 활용하는 내용도 많이 보였습니다.

 

그러다 python을 이용해 ppt를 자동으로 생성하는 방법을 소개한 곳이 있었는데, 실무에 적용한 사례라 참고하기 좋아 보였습니다. 

 

블로그) 코딩하는 인사팀 블로그) 파이썬으로 PPT 노가다 자동화 하기

 

파이썬으로 PPT 노가다 자동화 하기

대부분의 HR 시스템에서 인사 프로필 출력기능을 제공하지만, 인사담당자로 일하다 보면 직원들의 프로필을 PPT로 직접 만들어야 할 때가 자주 있습니다. 승진 심사, 해외 주재원 선발, 임원 평가

jgws.tistory.com

 

 

만들어서 어디에 써먹을거냐?

 

PPT를 프로그램을 통해 다룬다는 것은 DB나 파일, 혹은 API 등을 통해 얻은 데이터를 슬라이드를 만들 때 활용하는 것을 의미합니다.

한마디로 단순한 반복 작업을 줄여 보겠다는 의미인거죠... 

'귀찮음은 발명의 할배~'라는 얘기가 나올 법 합니다.

 

최근 생성AI의 등장은 그보다 더 고차원적인 작업도 가능하게 할 겁니다. 

하지만, 그건 주로는 내용과 관련된 측면이 더 클테니... 형식만 다루는 이번 작업과는 또 약간은 결이 다를 것 같긴 합니다. 

 

 

어떤 방식으로 할건데?

 

이번에는 Python-pptx 라는 패키지를 이용할 겁니다.

앞서 썼던 다른 글을 보신 분들은 아시겠지만 저는 패키지 자체의 세부 기능에는 관심이 별로 없습니다.

그걸 가지고 할 수 있는 '일'에 관심이 있을 뿐...

 

그래서 뭘 하는데?

 

퀴즈 100문제를 만들어 뒀는데 그걸 파워포인트 장표로 변환할 겁니다. 

 

처음에는 웹 어플리케이션으로 만들려고 했습니다.

그런데, 배경 음악을 깔려고 하니 최근 크롬에서는 자동 플레이 기능을 막아뒀다고 하더군요.

사용자의 액션 (예: 버튼 클릭 등)이 있어야 작동하도록 되어 있다는 얘기죠...

음악 배경을 손쉽게 깔 수 있는 방법을 고민해 보다가 파워포인트로 방향을 틀었습니다. 

 

이 방식을 취할 경우, 장점은 다음과 같습니다. 

  • 배경, 형태 디자인이 쉬움 (vs 웹 화면 개발) 
  • 자유도가 높음
  • 파워포인트 슬라이드에 애니메이션, 음악 배경 등 다양한 기능을 활용할 수 있음

 

시작해볼까요?

 

다음과 같은 과정을 통해 진행할 생각입니다. 

 

1.문제 100문제 생성

ChatGPT를 이용해 생성합니다. 파일 형식은 Json 파일로 다음과 같은 구조로 만듭니다. 

 

 

{
    "quizzes": [
      {"question": "하늘과 천둥의 신은?", "answer": "제우스"},
      {"question": "지혜와 전쟁의 여신은?", "answer": "아테나"},
      {"question": "바다의 신은?", "answer": "포세이돈"},
      {"question": "12가지 과업으로 유명한 영웅은?", "answer": "헤라클레스"},
      {"question": "음악과 예언의 신은?", "answer": "아폴로"},
      {"question": "사냥의 여신은?", "answer": "아르테미스"},
      {"question": "포도와 술의 신은?", "answer": "디오니소스"},
      {"question": "지하 세계의 신의 아내는?", "answer": "페르세포네"},
      ...
   ]
 }

 

 

2.기본 템플릿 역할의 파워포인트 준비

 

딸랑 2페이지짜리 PPT 파일을 만듭니다. 

첫페이지는 문제가 나올거고 두번째 페이지는 정답이 나올겁니다. 

문제와 정답 페이지 사이에는 3초 정도의 카운트다운이 진행되고 넘어가게 됩니다. 

첫 페이지에는 'no'(문제번호), 'question'(문제)가, 두번째 페이지에는 'answer'(정답) 값을 가진 텍스트 박스가 각각 있습니다. 

 

기본 파워포인트 파일은 맨 아래 부분의 이미지를 참고하시면 됩니다. 

 

 

3.파워포인트 파일 읽어 페이지 추가 

 

이 부분이 Python 코딩을 이용해 진행할 부분입니다. 

 

작업은 다음과 같은 순서로 진행됩니다.

 

  1.  1,2 페이지의 내용을 각각 읽습니다.
  2.  페이지를 복사해서 붙입니다.
  3.  슬라이드 상의 개체 내용 중 퀴즈 데이터를 이용해서 수정 변경합니다.
  4.  2, 3번 과정을 문항의 수만큼 반복합니다. 
  5.  전체 작업이 완료되고 나면 처음 참조한 2개의 페이지를 삭제합니다. 

 

이렇게 잡고 만들어본 전체 코드는 다음과 같습니다.

 

select_shape_by_text() 함수는 앞서 소개드린 블로그 글의 함수를 그대로 차용했고

copy_slide() 함수는 원본 코드가 배경 이미지 등을 제대로 가져오지 못하는 문제가 있어서 수정 보완했습니다. 

 

from pptx import Presentation
from pptx.util import Cm
from pptx.util import Inches
import pandas as pd
import os
import copy


if 1: # 퀴즈 데이터 읽어오기
    data = pd.read_json("quiz_data.json")
    df_quiz = pd.json_normalize(data['quizzes'])
    # print(df_quiz.head())

if 1: # PPT slide 관련 커스텀 함수 정의
    # 선택한 슬라이드 내의 text박스 선택 (text 박스의 text값 비교)
    def select_shape_by_text(slide, text):
        for x in slide.shapes:
            if x.has_text_frame and x.text == text:
                return x
        print('요청한 Shape를 찾을 수 없습니다.')
        
    # 선택한 슬라이드를 복제해서 추가
    # layout, 배경이미지 (별도), 기타 객체까지 포함
    def copy_slide(prs, index):
        import tempfile
        template = prs.slides[index]

        try:
            blank_slide_layout = prs.slide_layouts.get_by_name('빈 화면')
        except:
            blank_slide_layout = prs.slide_layouts[0]

        copied_slide = prs.slides.add_slide(blank_slide_layout)

        # 배경 이미지 복사
        for shape in template.shapes:
            if shape.shape_type == 13:  # shape_type 13은 이미지
                img_stream = shape.image.blob
                
                # 임시 파일 생성 (shapes.add_picture() 함수 사용을 위해... )
                with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as temp_file:
                    temp_file.write(img_stream)
                    temp_file_path = temp_file.name
                
                # 임시 파일을 사용하여 이미지 추가. 본 함수는 파일 경로를 필요로 함
                copied_slide.shapes.add_picture(temp_file_path, 0, 0, width=prs.slide_width, height=prs.slide_height)
                os.remove(temp_file_path)  # 임시 파일 삭제

        # 다른 shape 복사
        for shape in template.shapes:
            if shape.shape_type != 13:  # 이미지가 아닌 경우에만 복사
                elem = shape.element
                new_elem = copy.deepcopy(elem)
                copied_slide.shapes._spTree.insert_element_before(new_elem, 'p:extLst')

        return copied_slide    
    
    # 지정한 index의 slide를 삭제
    def del_slide(prs, index):
        xml_slides = prs.slides._sldIdLst  
        slides = list(xml_slides)
        xml_slides.remove(slides[index]) 
        
if 1: # PPT 파일 읽어 퀴즈 데이터 반영한 신규 slide 작성 및 추가 
    prs = Presentation('quiz_02.pptx')
    
    for i, r in df_quiz.iterrows():
        
        ## 퀴즈 페이지 작성
        slide_q = copy_slide(prs, 0)
        
        # 문항 번호 설정
        no = select_shape_by_text(slide_q, 'no').text_frame
        p_no = no.paragraphs[0]
        run_no = p_no.runs[0]
        run_no.text = f'Q{i+1}'
        
        # 문제 설정
        question = select_shape_by_text(slide_q, 'question').text_frame
        p_q = question.paragraphs[0]
        run_q = p_q.runs[0]
        run_q.text = r['question']
        
        
        ## 정답 페이지 작성
        slide_a = copy_slide(prs, 1)
        answer = select_shape_by_text(slide_a, 'answer').text_frame
        p_a = answer.paragraphs[0]
        run_a = p_a.runs[0]
        run_a.text = r['answer']
        
    # 기본 페이지 삭제
    del_slide(prs,0)
    del_slide(prs,0)
        
    # 파일 저장하기
    prs.save('quiz_02_result.pptx')

 

4.생성된 파워포인트 파일 확인

 

생성된 파일을 아주 단순한 내용이긴 하지만, 예상대로 잘 나와 주었습니다. 

참고로 좌측이 참조한 파워포인트 파일이고 우측이 코드를 이용해 생성된 파일입니다. 

사진 : 템플릿으로 사용한 PPT 파일과 코드를 이용해 자동 생성된 PPT 파일

 

 

 

생각보다 간단한 작업을 통해 귀찮은 반복 작업을 하지 않게 되어 좋네요.

앞서 얘기한 것처럼 이런 것도 MS Copilot을 이용하면 불필요하게 될 수도 있을 것 같긴 합니다. 

하지만, 이런 가성비 작업은 Copilot도 쉽게 넘보지 못하는 영역이 될 수도 있겠다는 생각도 듭니다.