📝

ChatGPT(gpt4o)でソフトウェア治具をさっと作る例1

2024/06/23に公開

ChatGPT(gpt4o)でソフトウェア治具をさっと作る例1

この例では、ChatGPT(GPT-4)を利用して、PDF結合ツール「PDF結合君」を作成します。
このツールは、複数のPDFファイルを1つに結合するための簡単なGUIアプリケーションです。

1. ツールの要件定義

まず、ツールの基本的な要件を定義しました。今回は以下の要件を設定しました。

  • 複数のPDFファイルを選択して1つのPDFに結合する
  • GUIを使用して直感的に操作できる
  • ツールはexeファイルとして配布可能にする

これに基づき、次のようなREADME.mdを作成しました。

# PDF結合君

### 説明
- これは、PyPDF2とtkinterを使用してPython 3で書かれたGUIツールです。
- 複数のPDFファイルを1つのPDFに結合できます。
- ユーザにはexeファイル形式で配布します。(pyInstallerを使用してexeファイルを作成します)

### 使用方法

1. exeファイルを起動します
2. 「元ファイル(複数)を選択」ボタンをクリックし、ファイル参照ダイアログで結合したいPDFファイルを選択します。
3. 必要に応じて「保存先ファイル名」テキストボックスに結合後のPDFファイル名を入力します。入力しない場合は、元のファイル1個目のファイル名に「_merged」が追加されたものになります。
4. 「結合」ボタンをクリックします。保存先ファイル名と同じ名前のファイルがすでに存在する場合、上書きするかどうかを確認するメッセージボックスが表示されます。

2. ChatGPTにコーディングを依頼

次に、ChatGPTにこの要件を元にして、コード生成を依頼します。

指示:

以下は、これから加工としているGUIツールのREADME.mdです。
main.pyを記述してください。

(ここに先ほどのREADME.mdの内容を貼り付け)

出力:

import tkinter as tk
from tkinter import filedialog, messagebox
import PyPDF2
import os

class PDFMergerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF結合君")
        
        # ファイルリスト
        self.pdf_files = []
        
        # 元ファイル(複数)を選択ボタン
        self.select_files_button = tk.Button(root, text="元ファイル(複数)を選択", command=self.select_files)
        self.select_files_button.pack(pady=10)
        
        # 保存先ファイル名の入力
        self.output_label = tk.Label(root, text="保存先ファイル名")
        self.output_label.pack()
        self.output_entry = tk.Entry(root, width=50)
        self.output_entry.pack(pady=5)
        
        # 結合ボタン
        self.merge_button = tk.Button(root, text="結合", command=self.merge_pdfs)
        self.merge_button.pack(pady=10)
    
    def select_files(self):
        files = filedialog.askopenfilenames(filetypes=[("PDF files", "*.pdf")])
        if files:
            self.pdf_files = list(files)
            messagebox.showinfo("ファイル選択", f"{len(files)} 個のファイルが選択されました。")
    
    def merge_pdfs(self):
        if not self.pdf_files:
            messagebox.showerror("エラー", "結合するPDFファイルを選択してください。")
            return
        
        output_filename = self.output_entry.get()
        if not output_filename:
            output_filename = os.path.splitext(os.path.basename(self.pdf_files[0]))[0] + "_merged.pdf"
        else:
            if not output_filename.endswith(".pdf"):
                output_filename += ".pdf"
        
        if os.path.exists(output_filename):
            if not messagebox.askyesno("上書き確認", f"{output_filename} は既に存在します。上書きしますか?"):
                return
        
        try:
            pdf_writer = PyPDF2.PdfWriter()
            
            for pdf_file in self.pdf_files:
                pdf_reader = PyPDF2.PdfReader(pdf_file)
                for page_num in range(len(pdf_reader.pages)):
                    page = pdf_reader.pages[page_num]
                    pdf_writer.add_page(page)
            
            with open(output_filename, "wb") as out:
                pdf_writer.write(out)
            
            messagebox.showinfo("成功", f"PDFファイルが正常に結合されました:{output_filename}")
        except Exception as e:
            messagebox.showerror("エラー", f"PDFファイルの結合中にエラーが発生しました:\n{e}")

if __name__ == "__main__":
    root = tk.Tk()
    app = PDFMergerApp(root)
    root.mainloop()

3. 追加仕様の実装

ちょっと使ってみて使いにくいところがあるので、以下の追加仕様を依頼します。

指示:

以下の仕様を追加してください

- 選択した元ファイルをListBoxで表示する
- ListBoxからファイルを選択して削除できる。全て削除するボタンもある。
- tkinterdnd2を使って、ListBoxにファイルをドラッグアンドドロップで追加できる
- 出力ファイル名も、ファイル参照ダイアログで指定できるようにする。デフォルトの出力先ディレクトリは元ファイル1個目と同じディレクトリにする。

上の例では一回で複数のことを依頼していますが、あまり複雑になりそうなら関連性の強い仕様をグルーピングして、指示を分けたほうが良いです。

ただし、指示を分けすぎると最初に言ったことを忘れたりするので、指示済みの仕様グループに追加したい内容がある場合は、以前の指示を変更して生成しなおさせるのが良いです。
(理想は、最初の指示に全ての仕様を含めて出力しなおしですが、若干のガチャ要素があるので、生成されるコードの量とかを加味していい塩梅を探るしかないです)

出力:

import tkinter as tk
from tkinter import filedialog, messagebox
from tkinterdnd2 import DND_FILES, TkinterDnD
import PyPDF2
import os

class PDFMergerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF結合君")
        
        # ファイルリスト
        self.pdf_files = []
        
        # 元ファイル(複数)を選択ボタン
        self.select_files_button = tk.Button(root, text="元ファイル(複数)を選択", command=self.select_files)
        self.select_files_button.pack(pady=10)
        
        # ファイルリスト表示
        self.file_listbox = tk.Listbox(root, selectmode=tk.SINGLE, width=50, height=10)
        self.file_listbox.pack(pady=5)
        
        # ListBoxからファイルを削除するボタン
        self.remove_file_button = tk.Button(root, text="選択したファイルを削除", command=self.remove_selected_file)
        self.remove_file_button.pack(pady=5)
        
        # 全てのファイルを削除するボタン
        self.clear_files_button = tk.Button(root, text="全てのファイルを削除", command=self.clear_files)
        self.clear_files_button.pack(pady=5)
        
        # ドラッグアンドドロップ
        self.root.drop_target_register(DND_FILES)
        self.root.dnd_bind('<<Drop>>', self.drop_files)
        
        # 保存先ファイル名の入力
        self.output_label = tk.Label(root, text="保存先ファイル名")
        self.output_label.pack()
        self.output_entry = tk.Entry(root, width=50)
        self.output_entry.pack(pady=5)
        
        # 保存先ファイル名を参照するボタン
        self.browse_output_button = tk.Button(root, text="保存先ファイルを選択", command=self.browse_output_file)
        self.browse_output_button.pack(pady=5)
        
        # 結合ボタン
        self.merge_button = tk.Button(root, text="結合", command=self.merge_pdfs)
        self.merge_button.pack(pady=10)
    
    def select_files(self):
        files = filedialog.askopenfilenames(filetypes=[("PDF files", "*.pdf")])
        if files:
            self.add_files(files)
    
    def drop_files(self, event):
        files = self.root.tk.splitlist(event.data)
        pdf_files = [file for file in files if file.endswith('.pdf')]
        self.add_files(pdf_files)
    
    def add_files(self, files):
        for file in files:
            if file not in self.pdf_files:
                self.pdf_files.append(file)
                self.file_listbox.insert(tk.END, file)
    
    def remove_selected_file(self):
        selected_index = self.file_listbox.curselection()
        if selected_index:
            index = selected_index[0]
            self.file_listbox.delete(index)
            del self.pdf_files[index]
    
    def clear_files(self):
        self.file_listbox.delete(0, tk.END)
        self.pdf_files.clear()
    
    def browse_output_file(self):
        output_file = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF files", "*.pdf")])
        if output_file:
            self.output_entry.delete(0, tk.END)
            self.output_entry.insert(0, output_file)
    
    def merge_pdfs(self):
        if not self.pdf_files:
            messagebox.showerror("エラー", "結合するPDFファイルを選択してください。")
            return
        
        output_filename = self.output_entry.get()


        if not output_filename:
            first_file_dir = os.path.dirname(self.pdf_files[0])
            output_filename = os.path.join(first_file_dir, os.path.splitext(os.path.basename(self.pdf_files[0]))[0] + "_merged.pdf")
        else:
            if not output_filename.endswith(".pdf"):
                output_filename += ".pdf"
        
        if os.path.exists(output_filename):
            if not messagebox.askyesno("上書き確認", f"{output_filename} は既に存在します。上書きしますか?"):
                return
        
        try:
            pdf_writer = PyPDF2.PdfWriter()
            
            for pdf_file in self.pdf_files:
                pdf_reader = PyPDF2.PdfReader(pdf_file)
                for page_num in range(len(pdf_reader.pages)):
                    page = pdf_reader.pages[page_num]
                    pdf_writer.add_page(page)
            
            with open(output_filename, "wb") as out:
                pdf_writer.write(out)
            
            messagebox.showinfo("成功", f"PDFファイルが正常に結合されました:{output_filename}")
        except Exception as e:
            messagebox.showerror("エラー", f"PDFファイルの結合中にエラーが発生しました:\n{e}")

if __name__ == "__main__":
    root = TkinterDnD.Tk()
    app = PDFMergerApp(root)
    root.mainloop()

4. 最後の確認と実行

コードが完成したら、実行して期待通りに動作することを確認します。また、pyInstallerを使用してexeファイルを生成することで、ユーザーが簡単にツールを使用できるようにしても良いかもしれません。

pyinstaller --onefile --windowed main.py

まとめ

今回は、ChatGPT(GPT-4)を使って簡単なPDF結合ツールを作成する手順を紹介しました。ChatGPTを活用することで、コーディングの手間を大幅に省くことができ、短時間で実用的なツールを作成することが可能です。

なお、この記事の下書きを生成するために以下のプロンプトを使いました。

ここまでの過程を、ブログ記事にまとめてください。
テーマは以下のとおりです。
- ChatGPT(GPT4o)を使って、簡単なツール(治具)をさっと作る手順の例

Discussion