「さはら3」です。
AI副業でどこまでいけるのか?をテーマに頑張っていきたいと思います。
本編
前回の記事ので作成したPDF簡単編集ツールソースコードの主要な部分の解説となります。
スクリプト全容
import tkinter as tk from tkinter import filedialog, messagebox import os import PyPDF2 def rotate_pdf(filepath, rotation, output_filepath): """指定した角度でPDFを回転させ、新しいファイルとして保存します。""" with open(filepath, 'rb') as file: reader = PyPDF2.PdfReader(file) writer = PyPDF2.PdfWriter() for page in reader.pages: page.rotate(rotation) writer.add_page(page) with open(output_filepath, 'wb') as output_file: writer.write(output_file) def split_pdf(input_filepath): """PDFをページごとに分割し、各ページを新しいファイルとして保存します。""" output_filepaths = [] with open(input_filepath, "rb") as file: reader = PyPDF2.PdfReader(file) for i, page in enumerate(reader.pages): writer = PyPDF2.PdfWriter() writer.add_page(page) base_name = os.path.basename(input_filepath) name_without_extension = os.path.splitext(base_name)[0] output_filepath = os.path.join(os.path.dirname(input_filepath), f"{name_without_extension}_page_{i+1}.pdf") with open(output_filepath, "wb") as output_file: writer.write(output_file) output_filepaths.append(output_filepath) return output_filepaths def merge_pdfs(filepaths, output_filepath): """複数のPDFファイルを1つのファイルに結合します。""" writer = PyPDF2.PdfWriter() for filepath in filepaths: with open(filepath, 'rb') as file: reader = PyPDF2.PdfReader(file) for page in reader.pages: writer.add_page(page) with open(output_filepath, 'wb') as output_file: writer.write(output_file) def on_action_click(action): """アクションボタンがクリックされたときの処理を行います。""" if not selected_files_list: messagebox.showwarning("警告", "PDFファイルを選択してください。") return if action == "merge": base_name = os.path.basename(selected_files_list[0]) name_without_extension = os.path.splitext(base_name)[0] output_filepath = os.path.join(os.path.dirname(selected_files_list[0]), f"{name_without_extension}_merged.pdf") merge_pdfs(selected_files_list, output_filepath) messagebox.showinfo("情報", f"PDFファイルを結合しました:{output_filepath}") elif action == "split": for filepath in selected_files_list: split_pdf(filepath) messagebox.showinfo("情報", "PDFファイルを分割しました。") elif action in ["rotate_90", "rotate_180", "rotate_270"]: rotation_map = {"rotate_90": 90, "rotate_180": 180, "rotate_270": 270} for filepath in selected_files_list: base_name = os.path.basename(filepath) name_without_extension = os.path.splitext(base_name)[0] output_filepath = os.path.join(os.path.dirname(filepath), f"{name_without_extension}_rotated_{rotation_map[action]}.pdf") rotate_pdf(filepath, rotation_map[action], output_filepath) messagebox.showinfo("情報", f"PDFファイルを{rotation_map[action]}度回転しました。") def open_file_dialog(): """ファイル選択ダイアログを開き、PDFファイルを選択します。""" listbox.delete(0, tk.END) # グローバルリストとラベルをクリアします selected_files_list.clear() selected_files_label.config(text="") filepaths = filedialog.askopenfilenames(title="PDFファイルを開く", filetypes=[("PDFファイル", "*.pdf")]) for filepath in filepaths: listbox.insert(tk.END, filepath) selected_files_list = [] # 選択されたファイルのリストを保持するグローバル変数 def on_select(event): """リストボックスの選択イベントを処理します。""" # リストボックスから選択されたアイテムを取得します selected_indices = event.widget.curselection() selected_items = [event.widget.get(i) for i in selected_indices] # グローバルリストから選択されていないアイテムを削除し、ラベルを更新します for item in selected_files_list[:]: # リストのコピーを作成してイテレーションします if item not in selected_items: selected_files_list.remove(item) # グローバルリストに新しく選択されたアイテムを追加します for item in selected_items: if item not in selected_files_list: selected_files_list.append(item) # グローバルリストにあるアイテムでラベルを更新します selected_files_label.config(text="\n".join(selected_files_list)) root = tk.Tk() root.title("PDFツール") root.geometry("600x500") # ルートウィンドウの幅を取得します window_width = root.winfo_screenwidth() root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) # リストボックスの幅を文字数で計算します(各文字が10ピクセル幅と仮定) listbox_width = window_width // 10 # リストボックスとそのスクロールバーのフレームを作成します frame1 = tk.Frame(root) frame1.grid(row=0, column=0, sticky="nsew") # 水平および垂直スクロールバーを作成します xscrollbar1 = tk.Scrollbar(frame1, orient=tk.HORIZONTAL) xscrollbar1.grid(row=1, column=0, sticky="ew") yscrollbar1 = tk.Scrollbar(frame1) yscrollbar1.grid(row=0, column=1, sticky="ns") # スクロールバー付きのリストボックスを作成します listbox = tk.Listbox(frame1, selectmode=tk.MULTIPLE, width=listbox_width, height=10,xscrollcommand=xscrollbar1.set, yscrollcommand=yscrollbar1.set) listbox.grid(row=0, column=0, sticky="nsew") # スクロールバーをリストボックスにスクロールさせるように設定します xscrollbar1.config(command=listbox.xview) yscrollbar1.config(command=listbox.yview) # frame1の行と列の設定を追加します frame1.grid_rowconfigure(0, weight=1) frame1.grid_columnconfigure(0, weight=1) BUTTON_WIDTH = 180 # ボタンの幅をピクセル単位で設定します # ファイルダイアログを開くボタンを作成します frame_open_button = tk.Frame(root, width=BUTTON_WIDTH, height=30) frame_open_button.grid_propagate(False) # サイズ変更を無効にします frame_open_button.grid(row=1, column=0, pady=10) open_button = tk.Button(frame_open_button, text="ファイルを選択", command=open_file_dialog) open_button.pack(fill=tk.BOTH, expand=True) # アクションボタンを作成します actions = [("90度回転", "rotate_90"), ("180度回転", "rotate_180"), ("270度回転", "rotate_270"), ("ページ分割", "split"), ("PDF結合", "merge")] for index, (button_text, action) in enumerate(actions): frame_button = tk.Frame(root, width=BUTTON_WIDTH, height=30) frame_button.grid_propagate(False) # サイズ変更を無効にします frame_button.grid(row=2+index, column=0, pady=5) button = tk.Button(frame_button, text=button_text, command=lambda act=action: on_action_click(act)) button.pack(fill=tk.BOTH, expand=True) # 選択されたファイルを表示するラベルを作成します List_label = tk.Label(root, text="処理対象ファイル", justify=tk.LEFT) List_label.grid(row=2+len(actions), column=0, pady=10, sticky="nsew") # 選択されたファイルを表示するラベルを作成します selected_files_label = tk.Label(root, text="", justify=tk.LEFT) selected_files_label.grid(row=3+len(actions), column=0, pady=10, sticky="nsew") # 両方のリストボックスに<<ListboxSelect>>イベントをバインドします listbox.bind('<<ListboxSelect>>', on_select) root.mainloop()
必要なモジュールのインポート
- 今回は、TkinterとPyPDF2がメインとなります。
import tkinter as tk from tkinter import filedialog, messagebox import os import PyPDF2
ページの回転処理
def rotate_pdf(filepath, rotation, output_filepath): """指定された角度でPDFを回転させ、新しいファイルとして保存します。""" with open(filepath, 'rb') as file: reader = PyPDF2.PdfReader(file) writer = PyPDF2.PdfWriter() for page in reader.pages: page.rotate(rotation) writer.add_page(page) with open(output_filepath, 'wb') as output_file: writer.write(output_file)
ページの分割処理
def split_pdf(input_filepath): """PDFを個別のページに分割し、各ページを新しいファイルとして保存します。""" output_filepaths = [] with open(input_filepath, "rb") as file: reader = PyPDF2.PdfReader(file) for i, page in enumerate(reader.pages): writer = PyPDF2.PdfWriter() writer.add_page(page) base_name = os.path.basename(input_filepath) name_without_extension = os.path.splitext(base_name)[0] output_filepath = os.path.join(os.path.dirname(input_filepath), f"{name_without_extension}_page_{i+1}.pdf") with open(output_filepath, "wb") as output_file: writer.write(output_file) output_filepaths.append(output_filepath) return output_filepaths
ページの結合処理
def merge_pdfs(filepaths, output_filepath): """複数のPDFファイルを1つのファイルに結合します。""" writer = PyPDF2.PdfWriter() for filepath in filepaths: with open(filepath, 'rb') as file: reader = PyPDF2.PdfReader(file) for page in reader.pages: writer.add_page(page) with open(output_filepath, 'wb') as output_file: writer.write(output_file)
各ボタンが押された際の処理
def on_action_click(action): """アクションボタンがクリックされたときの処理を行います。""" if not selected_files_list: messagebox.showwarning("警告", "PDFファイルを選択してください。") return if action == "merge": base_name = os.path.basename(selected_files_list[0]) name_without_extension = os.path.splitext(base_name)[0] output_filepath = os.path.join(os.path.dirname(selected_files_list[0]), f"{name_without_extension}_merged.pdf") merge_pdfs(selected_files_list, output_filepath) messagebox.showinfo("情報", f"PDFファイルを結合しました:{output_filepath}") elif action == "split": for filepath in selected_files_list: split_pdf(filepath) messagebox.showinfo("情報", "PDFファイルを分割しました。") elif action in ["rotate_90", "rotate_180", "rotate_270"]: rotation_map = {"rotate_90": 90, "rotate_180": 180, "rotate_270": 270} for filepath in selected_files_list: base_name = os.path.basename(filepath) name_without_extension = os.path.splitext(base_name)[0] output_filepath = os.path.join(os.path.dirname(filepath), f"{name_without_extension}_rotated_{rotation_map[action]}.pdf") rotate_pdf(filepath, rotation_map[action], output_filepath) messagebox.showinfo("情報", f"PDFファイルを{rotation_map[action]}度回転しました。")
ファイル選択ダイアログ
def open_file_dialog(): """ファイル選択ダイアログを開き、PDFファイルを選択します。""" listbox.delete(0, tk.END) # グローバルリストとラベルをクリアします selected_files_list.clear() selected_files_label.config(text="") filepaths = filedialog.askopenfilenames(title="PDFファイルを開く", filetypes=[("PDFファイル", "*.pdf")]) for filepath in filepaths: listbox.insert(tk.END, filepath) selected_files_list = [] # 選択されたファイルのリストを保持するグローバル変数
リストボックスのファイルを選択・解除したイベントの処理
- 今回の一番のポイントです。
def on_select(event): """リストボックスの選択イベントを処理します。""" # リストボックスから選択されたアイテムを取得します selected_indices = event.widget.curselection() selected_items = [event.widget.get(i) for i in selected_indices] # グローバルリストから選択されていないアイテムを削除し、ラベルを更新します for item in selected_files_list[:]: # リストのコピーを作成してイテレーションします if item not in selected_items: selected_files_list.remove(item) # グローバルリストに新しく選択されたアイテムを追加します for item in selected_items: if item not in selected_files_list: selected_files_list.append(item) # グローバルリストにあるアイテムでラベルを更新します selected_files_label.config(text="\n".join(selected_files_list))
アプリのレイアウト等の処理
root = tk.Tk() root.title("PDFツール") root.geometry("600x500") # ルートウィンドウの幅を取得します window_width = root.winfo_screenwidth() root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) # リストボックスの幅を文字数で計算します(各文字が10ピクセル幅と仮定) listbox_width = window_width // 10 # リストボックスとそのスクロールバーのフレームを作成します frame1 = tk.Frame(root) frame1.grid(row=0, column=0, sticky="nsew") # 水平および垂直スクロールバーを作成します xscrollbar1 = tk.Scrollbar(frame1, orient=tk.HORIZONTAL) xscrollbar1.grid(row=1, column=0, sticky="ew") yscrollbar1 = tk.Scrollbar(frame1) yscrollbar1.grid(row=0, column=1, sticky="ns") # スクロールバー付きのリストボックスを作成します listbox = tk.Listbox(frame1, selectmode=tk.MULTIPLE, width=listbox_width, height=10,xscrollcommand=xscrollbar1.set, yscrollcommand=yscrollbar1.set) listbox.grid(row=0, column=0, sticky="nsew") # スクロールバーをリストボックスにスクロールさせるように設定します xscrollbar1.config(command=listbox.xview) yscrollbar1.config(command=listbox.yview) # frame1の行と列の設定を追加します frame1.grid_rowconfigure(0, weight=1) frame1.grid_columnconfigure(0, weight=1) BUTTON_WIDTH = 180 # ボタンの幅をピクセル単位で設定します # ファイルダイアログを開くボタンを作成します frame_open_button = tk.Frame(root, width=BUTTON_WIDTH, height=30) frame_open_button.grid_propagate(False) # サイズ変更を無効にします frame_open_button.grid(row=1, column=0, pady=10) open_button = tk.Button(frame_open_button, text="ファイルを選択", command=open_file_dialog) open_button.pack(fill=tk.BOTH, expand=True) # アクションボタンを作成します actions = [("90度回転", "rotate_90"), ("180度回転", "rotate_180"), ("270度回転", "rotate_270"), ("ページ分割", "split"), ("PDF結合", "merge")] for index, (button_text, action) in enumerate(actions): frame_button = tk.Frame(root, width=BUTTON_WIDTH, height=30) frame_button.grid_propagate(False) # サイズ変更を無効にします frame_button.grid(row=2+index, column=0, pady=5) button = tk.Button(frame_button, text=button_text, command=lambda act=action: on_action_click(act)) button.pack(fill=tk.BOTH, expand=True) # 選択されたファイルを表示するラベルを作成します List_label = tk.Label(root, text="処理対象ファイル", justify=tk.LEFT) List_label.grid(row=2+len(actions), column=0, pady=10, sticky="nsew") # 選択されたファイルを表示するラベルを作成します selected_files_label = tk.Label(root, text="", justify=tk.LEFT) selected_files_label.grid(row=3+len(actions), column=0, pady=10, sticky="nsew") # 両方のリストボックスに<<ListboxSelect>>イベントをバインドします listbox.bind('<<ListboxSelect>>', on_select) root.mainloop()
いかがでしたでしょうか?
今回のポイントは、途中でも記載しましたが、リストの選択・解除で処理するファイルを指定した事です。
何度も何度もChatGPTさんとやり取りして、実装できました。
是非、実行してみてください。
AI関連は日進月歩、日々之精進でございます。
最後まで読んで頂きありがとうございました。
AIさはら
本日のAI着物美女
良かったらInstagramのフォローをお願いします。
https://www.instagram.com/ai_kimono_bijo/
非アダルトで運営しておりますので、職場でも安心して堪能いただけます。