PySimpleGUIを使って、デスクトップアプリケーションを自作
目的と背景
日々の業務でPDF資料を使うことが非常に多い。また、PDFの一部だけ切り取って、共有したいという機会も多く、PDFを開いて画面キャプチャしたりするのが手間。
Windows標準のエクスプローラーでも画像やPDFの中身を見ることは出来るが、他の情報(ディレクトリや、そのほかの実行ファイルなど)が多く扱いにくい。
完成品と動作
以下のような、GUIを作成。
タブ1は、任意のディレクトリの画像形式(pnh,jpeg)のみが一覧で表示され、クリックすると中身の画像が閲覧できる。
※サイズは任意サイズに変更可能
※都合の良いPDFファイルがなく自作…
タブ2は、任意のディレクトリのPDF形式(.pdf)のみが一覧で表示され、クリックすると全ページを画像形式に変換して、専用のフォルダを作成する。
(今回は、Dドライブに本アプリで分割した画像を管理する専用のフォルダを作成。元のPDFファイルの名称でフォルダが作成され、1枚ずつ画像として保存される。)
基本的な動きとしては、
・タブ2でPDFファイルを画像に変換して保存
・保存した画像をタブ1のビューワーで見る
参考にしたURL
PySimpleGUI公式ドキュメント
画像ビューワー部分は、上記ドキュメントの『Recipe - convert_to_bytes Function + PIL Image Viewer』のままです。
PDFを画像に変換する方法
Pythonでpdf2imageを使用しPDFファイルを画像に変換する
PDFファイルを画像変換する方法
必要なライブラリ、事前準備
ソースコード
import PySimpleGUI as sg
import os
import sys
from pdf2image import convert_from_path
# import PySimpleGUIQt as sg
import os.path
import PIL.Image
import io
import base64
def make_folder():
# デフォルトはDドライブ
base_folder_name = "D:/Popper_Media_Library" # ここは任意のフォルダにしてOK
# Dドライブにアプリケーション用のフォルダを作成
os.makedirs(base_folder_name, exist_ok=True)
# POPPLERライブラリの格納場所
POPPLER_PATH = r"poppler-0.68.0_x86\poppler-0.68.0\bin"
def main(file_path, full_file_path):
output_folder_name = "D:/Popper_Media_Library" + "/" + file_path
os.makedirs(output_folder_name, exist_ok=True)
folder_name = os.path.dirname(file_path)
pdf_file_name = os.path.basename(file_path)
base_file_name = os.path.splitext(pdf_file_name)[0]
print(base_file_name)
# PDFをImageに変換
pages = convert_from_path(full_file_path , poppler_path=POPPLER_PATH)
# 1ページずつ画像ファイルとして保存する
for i, page in enumerate(pages):
image_f_name = f'{base_file_name}_{str(i).zfill(2)}.png'
image_f_path = os.path.join(output_folder_name, image_f_name)
page.save(image_f_path )
# PySimpleGUI公式ドキュメントのまま
def convert_to_bytes(file_or_bytes, resize=None):
if isinstance(file_or_bytes, str):
img = PIL.Image.open(file_or_bytes)
else:
try:
img = PIL.Image.open(io.BytesIO(base64.b64decode(file_or_bytes)))
except Exception as e:
dataBytesIO = io.BytesIO(file_or_bytes)
img = PIL.Image.open(dataBytesIO)
cur_width, cur_height = img.size
if resize:
new_width, new_height = resize
scale = min(new_height/cur_height, new_width/cur_width)
img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.ANTIALIAS)
bio = io.BytesIO()
img.save(bio, format="PNG")
del img
return bio.getvalue()
# 画像ビューワータブの左側
tab1_left_col = [[sg.Text('Folder'), sg.In(size=(25,1), enable_events=True ,key='-FOLDER_Tab1-'), sg.FolderBrowse()],
[sg.Listbox(values=[], enable_events=True, size=(40,20),key='-FILE LIST_Tab1-')],
[sg.Text('Resize to'), sg.In(default_text="700",key='-W-', size=(5,1)), sg.In(default_text="500",key='-H-', size=(5,1))]]
# 画像ビューワータブの右側
tab1_images_col = [[sg.Text('You choose from the list:')],
[sg.Text(size=(40,1), key='-TOUT-')],
[sg.Image(key='-IMAGE-')]]
# PDF変換タブの左側
tab2_pdf_col = [[sg.Text('Folder'), sg.In(size=(25,1), enable_events=True ,key='-FOLDER_Tab2-'), sg.FolderBrowse()],
[sg.Listbox(values=[], enable_events=True, size=(40,20),key='-FILE LIST_Tab2-')]]
# PDF変換タブの右側
tab2_console = [[sg.Text('コンソール',size=(25,1))],
[sg.Multiline(default_text="hello\nworld\n!!", size=(40,20),key='-Console-')]]
# ----- 全体レイアウト -----
# タブ1のレイアウト
tab1_layout = [[sg.Column(tab1_left_col, element_justification='c'), sg.VSeperator(),sg.Column(tab1_images_col, element_justification='c')]]
# タブ2のレイアウト
tab2_layout = [[sg.Column(tab2_pdf_col, element_justification='c'), sg.VSeperator(),sg.Column(tab2_console, element_justification='c')]
]
# 全体のレイアウト
layout = [
[sg.TabGroup([[sg.Tab('画像ビュワー', tab1_layout), sg.Tab('PDF2PNG', tab2_layout)]])],
[sg.Quit()]
]
# --------------------------------- ウインドウの定義 ---------------------------------
window = sg.Window('Multiple Format Image Viewer', layout,resizable=True)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == '-FOLDER_Tab1-':
folder = values['-FOLDER_Tab1-']
try:
file_list = os.listdir(folder)
except:
file_list = []
fnames = [f for f in file_list if os.path.isfile(
os.path.join(folder, f)) and f.lower().endswith((".png", ".jpg", "jpeg", ".tiff", ".bmp"))]
window['-FILE LIST_Tab1-'].update(fnames)
elif event == '-FILE LIST_Tab1-':
try:
filename = os.path.join(values['-FOLDER_Tab1-'], values['-FILE LIST_Tab1-'][0])
window['-TOUT-'].update(filename)
if values['-W-'] and values['-H-']:
new_size = int(values['-W-']), int(values['-H-'])
else:
new_size = None
window['-IMAGE-'].update(data=convert_to_bytes(filename, resize=new_size))
except Exception as E:
print(f'** Error {E} **')
pass
if event == '-FOLDER_Tab2-':
folder = values['-FOLDER_Tab2-']
try:
file_list = os.listdir(folder)
except:
file_list = []
fnames = [f for f in file_list if os.path.isfile(
os.path.join(folder, f)) and f.lower().endswith((".pdf"))]
window['-FILE LIST_Tab2-'].update(fnames)
elif event == '-FILE LIST_Tab2-':
try:
file_path = os.path.join(values['-FOLDER_Tab2-'], values['-FILE LIST_Tab2-'][0])
window['-TOUT-'].update(file_path)
full_file_path = file_path
file_path = file_path.split(chr(92))[1]
print("File_Path は",file_path)
main(file_path, full_file_path)
console_text = file_path + "\n画像コンバートが完了しました"
window['-Console-'].update(console_text)
print("アップデート")
except Exception as E:
print(f'** Error {E} **')
pass
# --------------------------------- クローズ ---------------------------------
window.close()