Language/Python

[파이썬] tkinter (2) - PDF 페이지 추출 프로그램

nowkoes 2024. 6. 29. 18:24

tkinter 

개요

 

  지난 시간에는 GUI에서 기초적인 라벨을 생성하고 창을 띄우는 작업을 해보았다. 이번 시간에는 이미지를 삽입하고 버튼을 생성하여 이벤트를 할당하는 방법에 대해 알아보겠다. 또한 입력을 받는 Dialog를 생성하여입력을 받고, 이를 이용한 프로그램을 만들어 보겠다.


본문

위젯 - 백그라운드 이미지 설정

출처: https://pypi.org/project/pillow/

 

  오늘의 목표는 버튼을 클릭하면 PDF 파일을 선택하는 창이 뜨고, 해당 PDF 파일에서 특정 페이지를 추출하는 프로그램을 만드는 것이다. 먼저 그럴싸한 프로그램을 만들기 위해 백그라운드 이미지를 설정할 것이다. 이미지를 다루기 위해 다음과 같이 pillow 라이브러리를 설치하자.

 

pip install pillow

 

  PIL(Python Imaging Library) Python에서 이미지를 처리하고 조작하는 라이브러리다. 해당 라이브러리는 우리가 사용하는 tkinter 라이브러리와 상호 호환이 되므로, 이미지를 처리할 때 자주 사용한다.

 

import tkinter as tk
import os
from PIL import Image, ImageTk

root = tk.Tk()
base_path = os.getcwd()
file_name = "background.png"  # 이미지 파일 이름
width, height = 382, 363  # 오타 수정: hegiht -> height
root.title("Example")
root.resizable(False, False)
root.geometry(f"{width}x{height}")

 

  먼저 root 창을 생성하고 제목과 크기를 지정해 준다. 이때 resizable크기 수정을 변경하지 않게 하는 속성으로, 기초적인 UI 작업을 할 땐 반응형 위젯(크기가 변할 때, 이에 따라 동적으로 변경되는 구조)을 굳이 고려해주지 않아도 되므로, 이를 False로 설정해 주자. 또한 현재 작업 중인 폴더 내의 경로를 받기 위해 os.getcwd() 메서드를 사용하였다.

 

def SetImage(base_path, file_name):
    image_path = os.path.join(base_path, file_name)
    image = Image.open(image_path)
    return ImageTk.PhotoImage(image)
    
background_image = SetImage()

 

  다음은 백그라운드 이미지를 반환하는 SetImage 함수다. 이미지의 경로를 설정해 준 뒤, pillow 라이브러리의 Image 클래스를 이용하여 이미지 파일을 연 후, ImageTk의 PhotoImage로 반환해 주면 이미지가 지정이 된다.

 

canvas = tk.Canvas(root, width=width, height=hegiht)
canvas.pack(fill='both', expand=True)
canvas.create_image(0, 0, image=background_image, anchor="nw")

root.mainloop()

 

  canvas 위젯그림을 그리거나 복잡한 레이아웃을 구현하는 데 사용되는 메서드다. 선, 원, 다각형, 이미지, 텍스트 등의 다양한 그래픽 요소를 그릴 수 있으며, 일반적으로 백그라운드 이미지를 할당할 때 자주 사용된다. 따라서 캔버스의 크기와 할당될 위치를 지정한 후, 배치해 주면 된다. 이때 fill 속성위젯이 부모 위젯 내에서 어떻게 크기를 조정할지 지정하는 속성이다.


위젯 - 버튼 생성 및 이미지 지정

 

  이제 프로그램의 배경 화면을 설정했으니, 버튼을 생성하고 이벤트를 할당해 보자. 이때 이벤트사용자가 애플리케이션과 상호 작용할 때 발생하는 모든 동작을 의미하며, 여기서는 클릭 이벤트를 의미한다. 

 

from tkinter import filedialog, messagebox
from PyPDF2 import PdfReader, PdfWriter

def select_file():
    global selected_file_path
    while True:
        file_path = filedialog.askopenfilename(
            title="Select a PDF file",
            filetypes=(("PDF files", "*.pdf"), ("All files", "*.*"))
        )
        if not file_path:
            return

        if file_path.lower().endswith('.pdf'):
            selected_file_path = file_path
            print("Selected file:", file_path)
            break
        else:
            messagebox.showerror("Invalid File", "Please select a PDF file.")
            continue

def extract_file():
    if not selected_file_path:
        messagebox.showerror("Error", "No PDF file selected.")
        return

    start_page = simpledialog.askinteger("Input", "Enter the start page:", minvalue=1)
    end_page = simpledialog.askinteger("Input", "Enter the end page:", minvalue=start_page)

    if start_page is None or end_page is None:
        messagebox.showerror("Error", "Invalid page numbers.")
        return

    try:
        pdf_reader = PdfReader(selected_file_path)
        pdf_writer = PdfWriter()

        for page_num in range(start_page - 1, end_page):
            pdf_writer.add_page(pdf_reader.pages[page_num])

        output_file_path = filedialog.asksaveasfilename(
            title="Save Extracted PDF",
            defaultextension=".pdf",
            filetypes=(("PDF files", "*.pdf"), ("All files", "*.*"))
        )
        if not output_file_path:
            return

        with open(output_file_path, "wb") as output_file:
            pdf_writer.write(output_file)

        messagebox.showinfo("Success", f"Pages {start_page} to {end_page} extracted successfully.")
        
        os.startfile(output_file_path)
    except Exception as e:
        messagebox.showerror("Error", f"Failed to extract pages: {str(e)}")
        
select_button_name = "select.png"
extract_button_name = "extract.png"

select_button_image = SetImage(base_path, select_button_name)
extract_button_image = SetImage(base_path, extract_button_name)

button_select = tk.Button(root, image=select_button_image, command=select_file)
canvas.create_window(32, 235, anchor="nw", window=button_select)
button_extract = tk.Button(root, image=extract_button_image, command=extract_file)
canvas.create_window(222, 235, anchor="nw", window=button_extract)

 

 tkinter에서 버튼을 생성하기 위해 tk.Button 메서드를 활용할 수 있다. 이때 이미지를 지정할 수 있고, 버튼이 눌렸을 때 발생하는 이벤트를 command 인자에 할당하면 된다. 그리고 버튼이 눌렸을 때 파일을 선택하는 창을 띄울 수 있게 filedialog를 활용하였다. 또한 버튼을 선택한 후 Extract Page 버튼을 누르면 선택된 파일에서 페이지를 특정할 수 있게 구현하였다. 또한 PDF를 다루기 위해 PyPDF2 라이브러리를 사용하였다.

 

동작 영상

 

총합본

더보기
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
import os
from PIL import Image, ImageTk
from PyPDF2 import PdfReader, PdfWriter

def SetImage(base_path, file_name):
    image_path = os.path.join(base_path, file_name)
    image = Image.open(image_path)
    return ImageTk.PhotoImage(image)

def select_file():
    global selected_file_path
    while True:
        file_path = filedialog.askopenfilename(
            title="Select a PDF file",
            filetypes=(("PDF files", "*.pdf"), ("All files", "*.*"))
        )
        if not file_path:
            return

        if file_path.lower().endswith('.pdf'):
            selected_file_path = file_path
            print("Selected file:", file_path)
            break
        else:
            messagebox.showerror("Invalid File", "Please select a PDF file.")
            continue

def extract_file():
    if not selected_file_path:
        messagebox.showerror("Error", "No PDF file selected.")
        return

    start_page = simpledialog.askinteger("Input", "Enter the start page:", minvalue=1)
    end_page = simpledialog.askinteger("Input", "Enter the end page:", minvalue=start_page)

    if start_page is None or end_page is None:
        messagebox.showerror("Error", "Invalid page numbers.")
        return

    try:
        pdf_reader = PdfReader(selected_file_path)
        pdf_writer = PdfWriter()

        for page_num in range(start_page - 1, end_page):
            pdf_writer.add_page(pdf_reader.pages[page_num])

        output_file_path = filedialog.asksaveasfilename(
            title="Save Extracted PDF",
            defaultextension=".pdf",
            filetypes=(("PDF files", "*.pdf"), ("All files", "*.*"))
        )
        if not output_file_path:
            return

        with open(output_file_path, "wb") as output_file:
            pdf_writer.write(output_file)

        messagebox.showinfo("Success", f"Pages {start_page} to {end_page} extracted successfully.")
        
        os.startfile(output_file_path)
    except Exception as e:
        messagebox.showerror("Error", f"Failed to extract pages: {str(e)}")

root = tk.Tk()
selected_file_path = None
base_path = os.getcwd()
background_file_name = "background.png"
select_button_name = "select.png"
extract_button_name = "extract.png"

width, height = 382, 363
root.title("Example")
root.resizable(False, False)
root.geometry(f"{width}x{height}")

background_image = SetImage(base_path, background_file_name)
select_button_image = SetImage(base_path, select_button_name)
extract_button_image = SetImage(base_path, extract_button_name)

canvas = tk.Canvas(root, width=width, height=height)
canvas.pack(fill='both', expand=True)
canvas.create_image(0, 0, image=background_image, anchor="nw")

button_select = tk.Button(root, image=select_button_image, command=select_file)
canvas.create_window(32, 235, anchor="nw", window=button_select)
button_extract = tk.Button(root, image=extract_button_image, command=extract_file)
canvas.create_window(222, 235, anchor="nw", window=button_extract)

root.mainloop()

 

반응형

'Language > Python' 카테고리의 다른 글

[파이썬] GUI(tkinter) 멀티스레딩 with CAN Message  (1) 2024.09.27
[파이썬] 반응형 GUI  (0) 2024.09.23
[파이썬] tkinter (1)  (0) 2024.06.24
[Python] PDF 텍스트 추출  (0) 2023.10.31
[Python] matplotlib(1)  (0) 2023.09.12