Tkinter/ttkbootstrap(デスクトップGUI)

Tkinterとは

Tkinter は [[Python]] に標準で付属するGUIツールキットです。追加インストールなしにウィンドウ・ボタン・入力欄を作れるため、社内向けの小さなツールを配る用途で使われます。

import tkinter as tk

root = tk.Tk()
tk.Label(root, text="変換するファイルを選んでください").pack()
tk.Button(root, text="実行", command=lambda: run()).pack()
root.mainloop()

mainloop() はイベントループで、ここでウィンドウを保ちながらユーザーの操作を待ちます。この1行が返ってくるのはウィンドウを閉じたときです。

外観の古さが弱点でしたが、ttkbootstrap のようなテーマ集を被せると、コードをほぼ変えずに現代的な見た目にできます。

イベントループを止めない

GUI で最も多い不具合が「処理中に画面が固まる」です。原因は単純で、時間のかかる処理をイベントループと同じ流れで実行しているからです。

# 悪い例: 画面が固まる
def run():
    convert_all_files()   # 数十秒かかる

# 良い例: 別スレッドへ逃がす
def run():
    threading.Thread(target=convert_all_files, daemon=True).start()

ただし、別スレッドから画面部品を直接触ってはいけません。進捗を反映したいときは、結果をキューに入れてメインスレッド側で定期的に取り出す(after で自分自身を再スケジュールする)構成にします。

症状原因対処
ボタンを押すと固まる重い処理がイベントループを占有別スレッドへ逃がす
進捗が更新されない描画の機会が来ていないキュー経由でメインスレッドから更新
稀に落ちる別スレッドから部品を直接操作画面更新はメインスレッドに限定

配置は3方式を混ぜない

部品の配置には pack / grid / place の3方式があります。同じ親ウィジェットの中で混ぜると配置が壊れるため、どれを使うかを決めてから書き始めます。

方式向いている場面
pack上から順に積むだけの単純な画面
grid入力欄とラベルが並ぶフォーム状の画面
place座標を直接指定したいとき(可変サイズに弱い)

実務で最も使うのは grid です。行と列で位置を指定でき、ウィンドウのリサイズに追従させる設定(columnconfigure の重み付け)も素直に書けます。親が違えば方式を変えても構わないため、フレーム単位で使い分けるのが現実的な運用です。

CLIとGUIの使い分け

同じ処理を [[CLI設計(argparse・click)]] とGUIのどちらで提供するかは、使う人と繰り返しの有無で決めます。

  • 手順を自動化したい・他のツールとつなげたい → CLI
  • 操作する人がターミナルを使わない → GUI

現実的なのは、処理本体を関数として切り出し、CLIとGUIの両方から呼ぶ構成です。ロジックがどちらにも依存しなければ、後から入口を足せます。配布時は [[PyInstaller配布]] で単一実行ファイルにまとめると、Python未導入の端末へも渡せます。

関連技術とのつながり

  • [[Python]] — 標準ライブラリに含まれるため追加依存なしで始められる
  • [[PyInstaller配布]] — 作ったGUIをPython未導入の端末へ配るときの定番
  • [[CLI設計(argparse・click)]] — 同じ処理本体を別の入口から提供する選択肢
Q: TkinterのGUIで画面が固まる主な原因はどれ?
- [ ] ウィンドウのサイズが大きすぎる
- [x] 時間のかかる処理をイベントループと同じ流れで実行している
- [ ] ボタンの数が多すぎる
解説: `mainloop()` は操作を待ち続ける流れです。そこで重い処理を実行すると描画も操作も止まります。

Q: 別スレッドで重い処理を動かすときの注意点はどれ?
- [x] 別スレッドから画面部品を直接操作しない
- [ ] スレッドは必ず2つまでにする
- [ ] 処理が終わるまでウィンドウを非表示にする
解説: 画面の更新はメインスレッドに限定し、結果はキューなどを経由して受け渡します。

Q: 同じ処理をCLIとGUIの両方で提供したいときの構成はどれ?
- [ ] GUI側にロジックを書き、CLIから画面を起動する
- [x] 処理本体を関数として切り出し、CLIとGUIの両方から呼ぶ
- [ ] 同じコードを2つ複製して個別に保守する
解説: ロジックが入口に依存しなければ、後からどちらの入口も足せます。