Tkinterを使用したファイルオープナーアプリの作成

### はじめに
この記事では、Tkinterを使用してシンプルなファイルオープナーアプリを作成する方法を紹介します。アプリでは、ファイルの登録や閲覧が簡単に行えるように設計されています。以下に、コードとその説明を示します。

![2024-03-04 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3731846/95c4a35a-0283-d9a0-5468-a8e811f477ff.png)
![2024-03-04 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3731846/ebe2cd35-54ee-63c6-5a3c-778ae9af8eac.png)



```file_opener.py
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
import json
import subprocess
import os

class App:
    def __init__(self, root):
        self.root = root
        self.root.title("File Opener")
        # ウィンドウの大きさを設定
        self.root.geometry("500x230")
        # ウィンドウ全体にNotebookを広げる
        self.root.rowconfigure(0, weight=1)
        self.root.columnconfigure(0, weight=1)

        self.entries = []
        self.load_data()

        # ウィジェットの配置
        self.create_widgets()

        # ウィンドウが閉じられるときにデータを保存
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)

    def create_widgets(self):
        # ノートブック
        self.notebook = ttk.Notebook(self.root)
        self.notebook.grid(row=0, column=0, columnspan=3, rowspan=5, padx=5, pady=5, sticky="nsew")

        # 登録するタブ
        register_tab = ttk.Frame(self.notebook)
        self.notebook.add(register_tab, text="登録する")

        # 表示名とファイルパスのエントリー
        label_name = tk.Label(register_tab, text="表示名:")
        label_name.grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)

        label_path = tk.Label(register_tab, text="ファイルパス:")
        label_path.grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)

        label_params1 = tk.Label(register_tab, text="パラメータ1:")
        label_params1.grid(row=2, column=0, sticky=tk.W, padx=10, pady=5)

        label_params2 = tk.Label(register_tab, text="パラメータ2:")
        label_params2.grid(row=3, column=0, sticky=tk.W, padx=10, pady=5)

        label_color = tk.Label(register_tab, text="ボタンの色:")
        label_color.grid(row=4, column=0, sticky=tk.W, padx=10, pady=5)

        self.entry_name = tk.Entry(register_tab)
        self.entry_name.grid(row=0, column=1, padx=10, pady=5, ipadx=100)

        self.entry_path = tk.Entry(register_tab)
        self.entry_path.grid(row=1, column=1, padx=10, pady=5, ipadx=100)

        # ファイル選択ボタン
        select_button = tk.Button(register_tab, text="選択", command=self.browse_file)
        select_button.grid(row=1, column=2, padx=5, pady=5)

        # ボタンの色選択
        colors = ["", "yellow", "green", "blue"]
        self.selected_color = tk.StringVar()
        color_dropdown = ttk.Combobox(register_tab, textvariable=self.selected_color, values=colors, state="readonly")
        color_dropdown.grid(row=4, column=1, padx=10, pady=5, ipadx=90)
        color_dropdown.set("")

        # パラメータ入力エントリー
        self.entry_params1 = tk.Entry(register_tab)
        self.entry_params1.grid(row=2, column=1, padx=10, pady=5, ipadx=100)

        self.entry_params2 = tk.Entry(register_tab)
        self.entry_params2.grid(row=3, column=1, padx=10, pady=5, ipadx=100)

        # 登録ボタン
        register_button = tk.Button(register_tab, text="登録", command=self.register_entry)
        register_button.grid(row=5, column=1, pady=5)

        # 登録済みのボタンを表示するタブ
        self.show_tab = ttk.Frame(self.notebook)

        # 登録済みのボタンを表示
        self.show_registered_entries()

    def browse_file(self):
        file_path = filedialog.askopenfilename()
        self.entry_path.delete(0, tk.END)
        self.entry_path.insert(0, file_path)

    def register_entry(self):
        name = self.entry_name.get()
        path = self.entry_path.get()
        color = self.selected_color.get() if self.selected_color.get() else None
        params1 = self.entry_params1.get()
        params2 = self.entry_params2.get()

        if name and path:
            entry = {"name": name, "path": path}

            if color is not None:
                entry["color"] = color

            if params1:
                entry["params1"] = params1

            if params2:
                entry["params2"] = params2

            self.entries.append(entry)
            self.save_data()
            self.reset_and_show_entries()

            # エントリーをクリア
            self.entry_name.delete(0, tk.END)
            self.entry_path.delete(0, tk.END)
            self.selected_color.set("")
            self.entry_params1.delete(0, tk.END)
            self.entry_params2.delete(0, tk.END)

    def reset_and_show_entries(self):
        # 以前に登録されたボタンとタブを削除（最初のタブはスキップ）
        for tab in self.notebook.tabs()[1:]:
            self.notebook.forget(tab)

        # 再度表示
        self.show_registered_entries()

    def show_registered_entries(self):
        # 登録済みのボタンを表示
    
        # entriesリスト内の各エントリーに対して処理
        for i, entry in enumerate(self.entries):
            # グリッドの行と列を計算して、ボタンを適切な位置に配置
            row = i % 5
            column = i // 5
    
            # 新しいタブが必要な場合、新しいタブを作成
            if i % 25 == 0:
                self.show_tab = ttk.Frame(self.notebook)
                # タブをノートブックに追加
                self.notebook.add(self.show_tab, text=f"ボタンリスト ({i // 25 + 1})")
    
            # ボタンの表示名を取得
            button_text = entry["name"]
    
            # ボタンを作成し、対応するエントリーのファイルを開くコマンドを設定
            button = tk.Button(self.show_tab, text=button_text, command=lambda e=entry: self.open_file(e), width=10, height=1)
            
            # ボタンをグリッド上に配置
            button.grid(row=row, column=column, padx=5, pady=5)
    
            # エントリーに色情報がある場合、ボタンの背景色を設定
            if "color" in entry:
                button.config(bg="light" + entry["color"])

    def open_file(self, entry):
        # エントリーからファイルパスとパラメータを取得
        file_path = entry["path"]
        params1 = entry.get("params1", "")
        params2 = entry.get("params2", "")
    
        # フォルダの場合はフォルダを開く、それ以外は外部プログラムでファイルを開く
        if os.path.isdir(file_path):
            os.startfile(file_path)
        else:
            # 外部プログラムを呼び出してファイルを開く
            subprocess.run(["start", " ", file_path, params1, params2], shell=True)

    def load_data(self):
        try:
            with open("entries.json", "r") as file:
                self.entries = json.load(file)
        except FileNotFoundError:
            self.entries = []

    def save_data(self):
        with open("entries.json", "w") as file:
            # データを保存する際にensure_ascii=Falseを指定
            json.dump(self.entries, file, indent=2, ensure_ascii=False)
            # json.dump(self.entries, file, indent=2)

    def on_close(self):
        # ウィンドウが閉じられるときにデータを保存
        self.save_data()
        self.root.destroy()

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

各メソッドや処理の詳細な説明は以下になります。

1\. `__init__`: ウィンドウの初期設定やデータの読み込みを行います。
2\. `create_widgets`: ウィジェットの配置やタブの作成を行います。
3\. `browse_file`: ファイル選択ダイアログを表示して、選択されたファイルのパスをエントリーに設定します。
4\. `register_entry`: エントリーの登録処理を行います。エントリーの情報を取得し、entriesリストに追加します。
5\. `reset_and_show_entries`: 以前に登録されたボタンとタブを削除して再表示します。
6\. `show_registered_entries`: 登録済みのボタンを表示します。
7\. `open_file`: 登録されたエントリーに基づいてファイルを開く処理を行います。
8\. `load_data`: 以前に保存されたデータを読み込みます。
9\. `save_data`: 現在のエントリーリストをJSONファイルに保存します。
10\. `on_close`: ウィンドウが閉じられるときにデータを保存してアプリを終了します。

これにより、Tkinterを使用したシンプルなファイルオープナーアプリが完成します。