PythonでTkEasyGUIを使ってみた
Pythonでよく使われているGUIライブラリとしてPySimpleGUIがある。PySImpleGUIはバージョン5より商用ライセンスが有償となるなどの変更があった。本稿ではMITライセンスで作成されたTkEasyGUIについて使ってみたときの記録である。
過去プログラムの修正が必要だった点
多くの場面でPySimpleGUIと同じように書けば動作することが多いが、完全互換を目指しているわけでは無いそうなので、ところどころ違いがある。PySimpleGUIを使ったプログラムをいくつかTkEasyGUIに書き換えてみたときに修正が必要だった項目をメモとして残しておく。
- 省略された引数名の有無
- PySimpleGUIはよく使う引数(たとえばkeyやsize)に省略された名前が用意されていたが、TkEasyGUIは本来の名前に直す必要がある。
sg.Button(k="-start-")
→eg.Button(key="-start-")
- Textの幅パラメータ
- テキストウィジェットで幅を指定するときはsizeではなくwidthを使う。
sg.Text('テキスト', size=10)
→eg.Text('テキスト', width=10)
- イベントの発生
- イベントを発生させるにはWindow.write_event_valueメソッドではなく、Window.post_eventメソッドを使う
- このときイベントループ側でイベントの値を取得するとき、PySImpleGUIではvalue["イベント名"]のようにすると値が取得できたが、TkEasyGUIではvalueにイベントの値が入っている。
- プログレスバー
- TkEasyGUIでは2025/5/3時点ではプログレスバーが提供されていない? (見つけられなかった)
- テーマ
- eg.utils.set_themeメソッドが用意されているが、エラーにはならないが値を変えても違いがよく分からない。
- (2025/5/3追記) チェックボックスなどの見た目が変わることを確認。色味が変わるわけではなかった。今時ならvistaにしておくのがよさそう。
- カスタマイズされたボタンの提供
- PySimpleGUIではsg.OkでOKボタンが提供されていたが、そのようなものは提供されてなさそう。
基本的な利用例
一度表示するだけ
import TkEasyGUI as eg
layout = [
[eg.Text("メッセージ") ],
[eg.Button("閉じる") ],
]
window = eg.Window("タイトル", layout)
window.read()
イベントループ型
import TkEasyGUI as eg
layout = [
[eg.Text("1行目") ],
[eg.Text("2行目"), eg.InputText("input", key="inputsample") ],
[eg.Text("3行目"), eg.Button("Push", key="-buttonpush-") ],
]
window = eg.Window("タイトル", layout)
while window.is_alive():
readresult = window.read()
match readresult:
case '-buttonpush-', value:
inputvalue = value['inputsample']
eg.popup_non_blocking(f"値は{inputvalue}です", title="ボタン")
case _:
break
window.close()
window.is_alive()でウィンドウが生きている間イベントループを繰り返す。window.read()でイベントを読み込み、読み込んだイベントに応じた処理を行う。
コンテキストマネージャ(with)使用
import TkEasyGUI as eg
layout = [
[eg.Text("1行目") ],
[eg.Text("2行目"), eg.InputText("input", key="inputsample") ],
[eg.Text("3行目"), eg.Button("Push", key="-buttonpush-") ],
]
with eg.Window("タイトル", layout) as window:
for event in window.event_iter():
match event:
case '-buttonpush-', value:
inputvalue = value['inputsample']
eg.popup_non_blocking(f"値は{inputvalue}です", title="ボタン")
case _:
break
これまで見たとおり、レイアウトの指定方法や、基本的な構文はPySimpleGUIと概ね同じように使用できる。
タイムアウトイベント使用
import TkEasyGUI as eg
layout = [
[eg.Text("1行目") ],
[eg.Text("2行目"), eg.InputText("input", key="inputsample") ],
[eg.Text("3行目"), eg.Button("Push", key="-buttonpush-") ],
]
with eg.Window("タイトル", layout) as window:
for event in window.event_iter(timeout=10000, timeout_key="-timeout-"):
match event:
case '-buttonpush-', value:
inputvalue = value['inputsample']
eg.popup_non_blocking(f"値は{inputvalue}です。", title="ボタン")
case '-timeout-', _:
eg.popup(f"指定の時間内でイベントが発生しませんでした。", title="タイムアウトエラー")
break
case _:
break
イベント関連
イベントフック
イベントが発生したときに、イベントループで処理をする前に、何らかの処理を行わせることができる。window.register_event_hooksメソッドを使用し、イベントフックのdictをWindowに登録する。公式のサンプルを実行するのがわかりやすい。
引用元:https://github.com/kujirahand/tkeasygui-python/blob/main/tests/event_hooks.py
window.register_event_hooks(
{
# When pushing the `B1` button, the event hooks for `B1` will be executed.
"B1": [
lambda event, values: print_log("B1::hook#1:", event, values),
lambda event, values: print_log("B1::hook#2:", event, values),
lambda event, values: print_log("B1::hook#3:", event, values),
],
"B2": [
lambda event, values: print_log("B2::hook#1:", event, values),
lambda event, values: print_log("B2::hook#2:", event, values),
lambda event, values: True, # stop event propagation
lambda event, values: print_log("B2::hook#3:", event, values),
],
}
)
カスタムイベント
各エレメントであらかじめ定義されていないイベントも登録できる。よく使いそうなイベントは以下のあたり?
ButtonPress(Button), ButtonRelease, FocusIn, FocusOut, Enter, Leave, KeyPress(Key), KeyRelease, MouseWheel
全てのイベントはtkのドキュメントで確認できる(多分)。
bind使用
マウスオーバーしたら背景色を変えるサンプル
import TkEasyGUI as eg
inputbox = eg.InputText("ここに入力", key="-input-")
inputbox.bind("<Enter>", "enter")
inputbox.bind("<Leave>", "leave")
layout = [
[eg.Text("入力"), inputbox ],
[eg.Button("入力内容を表示", key="-button-"), ],
]
with eg.Window("タイトル", layout) as window:
for event in window.event_iter():
match event:
case '-button-', value:
inputvalue = value['-input-']
eg.popup(f"値は{inputvalue}です。", title="ボタン")
case '-input-enter', _:
window["-input-"].update(background_color="lightblue")
case '-input-leave', _:
window["-input-"].update(background_color="white")
case _:
break
bind_eventsで一括登録
マウスオーバーしたら背景色を変えるサンプル(2)
import TkEasyGUI as eg
inputbox = eg.InputText("ここに入力", key="-input-")
layout = [
[eg.Text("入力"), inputbox ],
[eg.Button("入力内容を表示", key="-button-"), ],
]
inputbox.bind_events({"<Enter>":"enter", "<Leave>":"leave"})
with eg.Window("タイトル", layout) as window:
for event in window.event_iter():
match event:
case '-button-', value:
inputvalue = value['-input-']
eg.popup(f"値は{inputvalue}です。", title="ボタン")
case '-input-enter', _:
window["-input-"].update(background_color="lightblue")
case '-input-leave', _:
window["-input-"].update(background_color="white")
case _:
break
ロングタスク
Window.start_threadを使う。
Window.start_thread(target:実行する関数, end_key:スレッドが終わったときに発生させるイベント名, 関数に渡す引数, ...)
入力値を使う時間がかかる処理をして、処理結果を表示する例を示す。
import time
import TkEasyGUI as eg
def longtask(inputvalue):
time.sleep(3)
return f"done {inputvalue}"
layout = [
[eg.Text("入力"), eg.Input("ここに入力", key="-input-"), ],
[eg.Button("入力内容を表示", key="-button-"), ],
]
with eg.Window("タイトル", layout) as window:
for event in window.event_iter():
match event:
case '-button-', value:
window["-input-"].update(readonly=True)
window["-button-"].update(disabled=True)
window.start_thread(longtask, "-end-process-", value["-input-"])
case '-end-process-', value:
window["-input-"].update(readonly=False)
window["-button-"].update(disabled=False)
eg.popup(f"処理結果は{value['-end-process-']}です。", title="処理終了")
case _:
break