見出し画像

TkEasyGUIライブラリの基本とサンプルコード解説

TkEasyGUIは記述方法が分かりやすく簡単にGUIが構築ができます。
もしPySimpleGUIを使ったことのある人ならすぐに使えるようになるでしょう。


TkEasyGUI概要

・TkEasyGUIは、Pythonで最も簡単にGUIアプリケーション開発するライブラリです。
・Tkinterのような従来のUIライブラリが持つ複雑さを解消し、より多くの開発者がGUIアプリの開発を楽しめます。
・本ライブラリは、簡単にGUIを構築できるライブラリPySimpleGUIの概念を引き継ぎつつ、独自の機能を追加しています。

TkEasyGUI公式

特徴

・TkEasyGUIは、GUIアプリケーションを簡単かつシンプルに作成することができるPythonライブラリです。
・イベントモデルでは、よく知られたGUIライブラリPySimpleGUIと互換性があります。
・Pythonの標準UIライブラリTkinterは、学習障壁が高と考えられていますが、このライブラリを使用すると、GUIアプリケーションを直感的に作成できます。
・型ヒントに対応しているので、コード補完でプロパティを選択できます。(本パッケージの利用には、Python 3.9以降が必要です。)
・ライセンスには比較的緩いMITライセンスを採用しています。将来このライセンスを変えることはありません。

TkEasyGUI公式

基本となる書き方

実行環境
・OS:Windows
・python:3.11.9
・TkEasyGUI:0.2.73

venvを使った仮想環境の作成方法

はじめに仮想環境の作成

python -m venv .venv

仮想環境に入る

.venv\Scripts\activate

pipを使ったTkEasyGUIのインストール

pip install TkEasyGUI


TkEasyGUIでできる最小のコード

import TkEasyGUI as eg
name = eg.input("あなたの名前は?")
eg.print(f"こんにちは、{name}。")

名前を入力し、OKボタンを押すとメッセージが表示されます。


テキストとボタンのシンプルなウィンドウを作成するコード

import TkEasyGUI as eg

# ウィンドウのレイアウトを定義
layout = [
    [eg.Text("Hello, World!")],
    [eg.Button("OK")]
]

# ウィンドウを作成し、with文を使用してコンテキスト管理
# "Hello App"はウィンドウのタイトル、layoutは上で定義したレイアウト
with eg.Window("Hello App", layout) as window:
    # イベントループ
    for event, values in window.event_iter():
        # "OK"ボタンがクリックされた場合
        if event == "OK":
            # ウィンドウに"Thank you."というメッセージを表示
            eg.print("Thank you.")
            break

PySimpleGUIに似たコードで記述できます。

import TkEasyGUI as eg

# ウィンドウのレイアウトを定義
layout = [
    [eg.Text("Hello, World!")], 
    [eg.Button("OK")]
]

# ウィンドウを作成
# "Hello App"はウィンドウのタイトル、layoutは上で定義したレイアウト
window = eg.Window("Hello App", layout)

# イベントループ
while True:
    # イベントとその関連値を返します
    # eventにはボタンクリックなどのイベント名、valuesには入力値などが辞書データで格納されます
    event, values = window.read() # イベントとその値を読み取る
    # "OK"ボタンがクリックされたか、ウィンドウを閉じた場合
    if event in ["OK", eg.WINDOW_CLOSED]:
        eg.popup("Thank you.")
        break
# ウィンドウを閉じる
window.close()

どちらのコードでも同じ動作をします。
テキストに「Hello, World!」と表示し、OKボタンを押すと「Thank you.」と書いたしたポップアップを表示します。

TkEasyGUIのサンプルコード

2024/08/08までにあった公式サンプルコードに実行内容がわかりやすいコメントと実行画像を載せています。
※各ウィジェット(Text、Buttonなど)にフォーカスを当てたサンプルコードは載せていません。

作成した仮想環境のあるディレクトリにTkEasyGUIのサンプルコードをGitHubからクローンするかzipファイルをダウンロードします。

clone https://github.com/kujirahand/tkeasygui-python.git

サンプルコードは「tkeasygui-python/tests」にあります。

見たほうがいいサンプルコード

順番通りに作成しています。

  • ui_test.py : 基本のUI

  • event_loop_test.py : テキストとボタンだけのシンプルなGUI

  • login_password.py : テキスト隠し

  • simple_text.py : テキスト表示

  • set_readonly.py : 読み取り専用設定テスト

  • slider_test.py : スライダーテスト

  • many_buttons.py : たくさんのボタン

  • popup_test.py : 各ポップアップ/ダイアログテスト

  • filebrowse_test.py : ファイルとフォルダの選択機能テスト

  • listbrowse_test.py : ブラウズボタン

  • image_test.py : 画像の表示

    • graph_image.py : アニメーション

    • opencv_camera.py : カメラ

  • keep_on_top.py : ウィンドウの手前固定表示

  • window_key_events.py : キーボードキーの取得

  • menu_test.py : メニューバー

  • paint_eg.py : TkEasyGUI用ペイント

    • paint_compatible.py : 書き方が違うペイント

  • column_test.py : カラムレイアウト

  • canvas_test.py : 図形表示

  • calc.py : 簡易的な電卓

  • event_hooks.py : イベントフック


TkEasyGUIサンプルを選択し起動するGUI

ほかのサンプルを簡単に起動できるGUI
これを使ってGUIの動きを確認できます。

※GitHubからダウンロードしたコードから実行をしてください。

実行したいファイル名を左のリストをクリックし、実行をクリック
右には選択ファイルのコードが表示されます。

コードは以下の通りになります。
公式サンプル file_viewer.py

import os
import subprocess
import sys
from threading import Thread

# import PySimpleGUI as sg
import TkEasyGUI as sg

# set path
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
#TARGET_DIR = os.path.join(ROOT_DIR, "tests")
TARGET_DIR = ROOT_DIR
# get font
font = ("Arial", 20 if sg.is_mac() else 12)

def get_program_files():
    """プログラムファイルのリストを取得する関数"""
    # files = os.listdir(ROOT_DIR)
    files = []
    for root, dirs, file_list in os.walk(TARGET_DIR):
        for f in file_list:
            full = os.path.join(root, f)
            parts = full[len(TARGET_DIR)+1:]
            files.append(parts)
            print("-", parts)
    files = [f for f in files if f.endswith('.py')] # filter
    files = list(sorted(files))
    return files

def run_program(filename):
    """指定されたPythonプログラムを実行する関数"""
    file_dir = os.path.dirname(filename)
    subprocess.run([sys.executable, filename], cwd=file_dir)

# ウィンドウのレイアウトを定義
layout = [
    [
        sg.Text("TkEasyGUI samples:"),
        sg.Text(f"(TkEasyGUI v.{sg.__version__})")
    ],
    [
        # ファイルリストを表示するリストボックス
        sg.Listbox( 
            values=get_program_files(), 
            size=(30, 20), 
            key="-files-", # 要素の参照キー
            enable_events=True, # アクションがあれば実行する引数
        ),
        sg.VSeparator(pad=5),# 縦区切り線
        # ファイル内容を表示する複数行テキストボックス
        sg.Multiline(
            size=(40, 20),
            key="-body-", # 要素の参照キー
            # それぞれをコメントアウトし、実行すれば動きがわかります
            expand_y=True, # 上下を詰める
            expand_x=True # 左右を詰める
        )
    ],
    [sg.HSeparator(pad=5)],# 横区切り線
    [
        sg.Button(sg.get_text("Run"), key="-run-", size=(10, 1)),
        sg.Button(sg.get_text("Close"), key="-close-"),
    ],
]

window = sg.Window("Python Viewer", layout, font=font)

# イベントループ
while True:
    event, values = window.read() # イベントとその値を読み取る
    print("#", event, values)
    
    # ウィンドウを閉じる
    if event in [sg.WINDOW_CLOSED, "-close-"]:
        break
    
    # 'Run'ボタンが押された場合
    if event == "-run-":
        files = values["-files-"]
        if len(files) > 0:
            filename = values["-files-"][0]
            fullpath = os.path.join(TARGET_DIR, filename)
            # 別スレッドでプログラムを実行(UIをブロックしない)
            Thread(target=run_program, args=(fullpath,)).start()
    
    # ファイルリストで選択が変更された場合
    if event == "-files-":
        files = values["-files-"]
        if len(files) > 0:
            filename = values["-files-"][0]
            fullpath = os.path.join(TARGET_DIR, filename)
            with open(fullpath, "r", encoding="utf-8") as f:
                text = f.read()
                # アップデートを使い Multiline にテキストをアップデート
                window["-body-"].update(text)
window.close()

基本のUI

基本となるウィジェットの動きを確認すことができるもの。

コードは以下の通りになります。
公式サンプル ui_test.py

"""
ui test
"""
# import PySimpleGUI as sg
import TkEasyGUI as sg

# window create
window = sg.Window("UI test", layout=[
    # テキスト
    [sg.Text(
        "TkEasyGUI Test:" # ラベル
    )],
    # テキスト入力
    [sg.Input(
        "input1", # テキスト
        key="-input1-", # 要素の参照キー
        enable_events=True # アクションがあれば実行する引数
    )],
    # 文字を隠すテキスト入力
    [sg.Input(
        "input2", # テキスト
        key="-input2-", # 要素の参照キー
        password_char="*" # 文字を隠す文字
    )],
    # 複数行入力テキスト
    [sg.Multiline(
        "multiine", # テキスト
        key="-multiline-", # 要素の参照キー
        enable_events=True, # アクションがあれば実行する引数
        size=(40, 2) # 縦横サイズ
    )],
    # スライダー
    [sg.Slider(
        key="-slide-", # 要素の参照キー
        enable_events=True, # アクションがあれば実行する引数
        orientation="h", # 水平(horizontal)
        range=(0, 100), # 範囲
        default_value=50, # デフォルトの数値
        expand_x=True # 水平方向に広げる
    )],
    # リストボックス、テキスト
    [
        sg.Listbox(
            ["list1", "list2", "list3", "list4"], # リストボックスの値リスト
            key="-listbox-", # 要素の参照キー
            enable_events=True, # アクションがあれば実行する引数
            select_mode=sg.LISTBOX_SELECT_MODE_EXTENDED # リストの選択モード 使われているのは`extended`(multiple, browse, extended, single)
        ),
        sg.Text(
            "-", # ラベル
            key="-listbox-text-" # 要素の参照キー
    )],
    # ラジオボタン
    [
        sg.Radio(
            "Radio1", # ラベル
            group_id="abc", # グループID
            key="-radio1-", # 要素の参照キー
            enable_events=True # アクションがあれば実行する引数
        ),
        sg.Radio(
            "Radio2", # ラベル
            group_id="abc", # グループID
            key="-radio2-", # 要素の参照キー
            default=True, # デフォルトの選択
            enable_events=True # アクションがあれば実行する引数
        )
    ],
    # チェックボックス
    [
        sg.Checkbox(
            "Checkbox", # ラベル
            key="-checkbox-", # 要素の参照キー
            enable_events=True # アクションがあれば実行する引数
        ), 
        sg.Button(
            "Change", # ラベル
            key="-checkbox-button-" # 要素の参照キー
        )
    ],
    # コンボボックス
    [
        sg.Combo(
            ["combo1", "combo2", "combo3"], # コンボボックスの値リスト
            default_value="combo1", # デフォルトの値
            key="-combo-",  # 要素の参照キー
            enable_events=True # アクションがあれば実行する引数
        ), 
        sg.Button(
            "Change",  # ラベル
            key="-combo-button-" # 要素の参照キー
        )
    ],
    # ボタン
    [sg.Button("Exit"), sg.Button("Maximize"), sg.Button("Minimize"), sg.Button("Hide"), sg.Button("UnHide")],
], font=("Arial", 12), finalize=True, resizable=True)
# event loop
while True:
    event, values = window.read()
    print("#", event, values)
    if event in (None, "Exit", sg.WINDOW_CLOSED):
        break
    if event == "Maximize":
        # ウィンドウの最大化
        window.maximize()
        
    if event == "Minimize":
        # ウィンドウの縮小化
        window.minimize()
        
    if event == "Hide":
        # ウィンドウを隠す
        window.hide()
        
    if event == "UnHide":
        # ウィンドウの非表示を解除
        window.un_hide()
    
    # listbox内のものを選択した場合
    if event == "-listbox-":
        # リストボックスの値を取得し文字列と結合
        selected = " selected: " + "/".join(values["-listbox-"])
        
        # 結果をテキスト(-listbox-text-キー)要素に表示
        window["-listbox-text-"].update(text=selected)
    
    # -checkbox-button- キーのボタンが押された場合
    if event == "-checkbox-button-":
        # チェックボックスの現在の状態を取得
        b = window["-checkbox-"].get()
        
        # チェックボックスの状態を反転し、テキストを"Changed"に更新
        window["-checkbox-"].update(value=(not b), text="Changed")
    
    # -combo-button- キーのボタンが押された場合
    if event == "-combo-button-":
        # コンボボックス(-combo-キー)の値を"combo3"に変更
        window["-combo-"].update(value="combo3")
window.close()

シンプルなGUI

TkEasyGUIの基本の形。

コードは以下の通りになります。
公式サンプル event_loop_test.py

import TkEasyGUI as eg

# ウィンドウのレイアウトを定義
layout = [
    [eg.Text(
        "Hello, World!" # ラベル
        )],
    [eg.Button(
        "Exit" # ラベル
        )]
]
# ウィンドウの作成
window = eg.Window("test", layout)

# イベントループ
for event, values in window.event_iter():
    if event == "Exit":
        # ポップアップの表示
        eg.popup("Thank you.")
        break

テキスト隠しGUI

テキストボックス(Input)に入力される文字を任意の文字で隠すことができます。

コードは以下の通りになります。
公式サンプル login_password.py

import TkEasyGUI as sg

# define layout
layout=[
    [sg.Text("User Id:")],
    [
        sg.Input(
            "test@example.com", # テキスト
            key="-userid-", # 要素の参照キー
            enable_events=True # アクションがあれば実行する引数
        )
    ],
    [sg.Text("Password:")],
    [
        sg.Input(
            "abcdefg", # テキスト
            key="-password-", # 要素の参照キー
            password_char="*" # 文字を隠す一つの文字
        )
    ], # 引数に password_char を使うと文字を隠す
    [sg.Button("OK"), sg.Button("Cancel")],
]
# window create
window = sg.Window("Login dialog", layout=layout)
# window.move_to_center()

# イベントループ
while window.is_alive():
    event, values = window.read()
    print("#", event, values)
    if event == "Cancel":
        break
    if event == "OK":
        print(window.get_location())
        sg.popup("OK, " + values["-userid-"])
window.close()

テキスト表示GUI

テキストを表示する。

コードは以下の通りになります。
公式サンプル simple_text.py

import TkEasyGUI as sg

# ウィンドウのレイアウトを定義
layout = [[sg.Text(
    "For wisdom is better than corals;\n" + 
    "All other desirable things cannot compare to it."
)]]
window = sg.Window("Proverb", layout)

# イベントループ
while True:
    # イベントとその値を読み取る
    event, values = window.read()
    # close button
    if event == sg.WINDOW_CLOSED:
        break
    # check OK Button
    # if event == "OK":
    #     break
window.close()

読み取り専用設定テストGUI

テキストボックスを読み取り専用をテストする。

「Readonly(tkeasygui)」ボタンを押すことでテキストボックスを読み取り専用に変化させています。

「Change」を押すと、すべての文字変化します。

コードは以下の通りになります。
公式サンプル set_readonly.py

import TkEasyGUI as eg

# import PySimpleGUI as eg

# フォントの設定(Arial, サイズ20)
a_font = ("Arial", 20)

window = eg.Window("BasicUI", layout=[
    [eg.Button("Change", key="-change-", font=a_font),eg.Button("Readonly(tkeasygui)", font=a_font)],
    [eg.Text("Text", key="-text-", font=a_font)],
    [eg.Input("Input", key="-input-", font=a_font)],
    [eg.Multiline("Multiline", key="-multiline-", font=a_font)],
])

# 読み取り専用モードのフラグ(初期値:False)
readonly = False

# イベントループ
while True:
    event, values = window.read()
    if event == eg.WINDOW_CLOSED:
        break
    print(event, values)
    if event == "-change-":
        print("change")
        # 各要素のテキストを "changed!!" に更新
        window["-change-"].update("changed!!")
        window["-text-"].update("changed!!")
        window["-input-"].update("changed!!")
        window["-multiline-"].update("changed!!")
    if event == "Readonly(tkeasygui)":
        
        # 読み取り専用モードの切り替え
        readonly = not readonly
        
        # 入力フィールドの読み取り専用状態を更新
        window["-input-"].update(readonly=readonly)
        window["-multiline-"].update(readonly=readonly)
window.close()

スライダーテストGUI

スライダーの動きを確認できるもの。

コードは以下の通りになります。
公式サンプル slider_test.py

# import PySimpleGUI as eg
import TkEasyGUI as eg

# define layout
layout = [
    [eg.Text("-slider1-")],
    # 基本のスライドバー
    [eg.Slider(
        key="-slider1-", # 要素の参照キー
        range=[0, 100], # 範囲
        size=[30, 2], # 縦横サイズ
        orientation="h", # 水平(horizontal)
        default_value=50, # デフォルトの数値
        resolution=0.1, # 動かせるスライドの単位
        tick_interval=10, # 表示する間隔の数値
        enable_events=True # アクションがあれば実行する引数
    )],
    [eg.HSeparator()], # 横線
    [eg.Text("-slider2- (disable_number_display=True)")],
    [
        # 数値の無いスライドバー
        eg.Slider(
            key="-slider2-", # 要素の参照キー
            range=[0, 100], # 範囲
            size=[30, 2], # 縦横サイズ
            orientation="h", # 水平(horizontal)
            default_value=0, # デフォルトの数値
            resolution=1, # 動かせるスライドの単位
            enable_events=True, # アクションがあれば実行する引数
            disable_number_display=True # 数字の表示(False)/非表示(True)
        ),
        eg.Text("0.0", key="-text2-"),
    ],
    [eg.Button("Change to 20"), eg.Button("Check Value")],
    [eg.HSeparator()],# 横線
    [eg.Text("-slider3-")],
    [
        # 縦向きのスライドバー
        eg.Slider(
            key="-slider3-", # 要素の参照キー
            range=[0, 100], # 範囲
            size=[2, 30], # 縦横サイズ
            orientation="v", # 垂直(vertical)
            default_value=50, # デフォルトの数値
            resolution=0.1, # 動かせるスライドの単位
            disable_number_display=False, # 数字の表示(False)/非表示(True)
            enable_changed_events=True # 変更したイベントの実行
        ),
        eg.VSeparator(), # 縦線
        eg.Multiline("", key="-output-", size=[50, 20]),
    ],
    [eg.Button("Change range(0,10)"), eg.Button("Change range(0,100)")],
    [eg.HSeparator()],# 横線
    [eg.Button("Exit")]
]


# create a window
window = eg.Window("Slider Test", layout)

# イベントループ
while True:
    event, values = window.read()
    print(f"event={event} || values={values}")
    if event in ["Exit", eg.WIN_CLOSED]:
        break
    if event == "-slider2-":
        # slider2が動いたときに値の取得
        v = values["-slider2-"]
        
        # 取得した値をテキストに更新
        window["-text2-"].update(str(v))
        
    # `Change to 20`ボタンが押された場合
    if event == "Change to 20":
        # `slider1` `slider2` `text2` の値を20に更新
        window["-slider1-"].update(value=20)
        window["-slider2-"].update(value=20)
        window["-text2-"].update("20.0")
    
    # `Change range(0,10)`ボタンが押された場合
    if event == "Change range(0,10)":
        # slider3の最大の値を 0から10 に更新
        window["-slider3-"].update(range=[0, 10])
        
    # `Change range(0,100)`ボタンが押された場合
    elif event == "Change range(0,100)":
        # slider3の最大の値を 0から100 に更新
        window["-slider3-"].update(range=[0, 100])
        
    # 各スライダーの値を Multiline に書き込む
    s = "[event=" + event + "]\n------\n"
    s += f"-slider1-: {values['-slider1-']}\n"
    s += f"-slider2-: {values['-slider2-']}\n"
    s += f"-slider3-: {values['-slider3-']}\n"
    s += f"range=[{window['-slider3-'].get_range()}]\n"
    window["-output-"].update(s)
  • enable_events は随時イベント処理が行われる

    • slider1ではスライドを動かす度にイベントが発生し、複数行テキストボックスの数字が随時変更される

  • enable_changed_events は最終のイベントのみ処理が行われる

    • slider3ではスライドを動かしても複数行テキストボックスに変化はないが、カーソルを離すと複数行テキストボックスに変更が加えられる


たくさんのボタンGUI

たくさんのボタンを並べる。
押されたボタンは2回目押せないように無効にしている。

コードは以下の通りになります。
公式サンプル many_buttons.py

import TkEasyGUI as sg

# define layout --- make 12 buttons
layout = []
for row in range(3):
    layout.append([])
    for col in range(4):
        no = row*4+col+1
        btn = sg.Button(str(no), key=f"-button{no}",
                        size=(3, 1),
                        etadata={"no": no}) # ボタンにメタデータを追加
        layout[row].append(btn)
# add close button
layout.append([sg.HSeparator()]) # 横線
layout.append([sg.Button("Close")])

# 最終的なレイアウト
# layout = [
#     [sg.Button(str(1), key=f"-button1",size=(3, 1), metadata={"no": 1}), sg.Button(str(2), key=f"-button2",size=(3, 1), metadata={"no": 2}), sg.Button(str(3), key=f"-button3",size=(3, 1), metadata={"no": 3}), sg.Button(str(4), key=f"-button4",size=(3, 1), metadata={"no": 4})],
#     [sg.Button(str(5), key=f"-button5",size=(3, 1), metadata={"no": 5}), sg.Button(str(6), key=f"-button6",size=(3, 1), metadata={"no": 6}), sg.Button(str(7), key=f"-button7",size=(3, 1), metadata={"no": 7}), sg.Button(str(8), key=f"-button8",size=(3, 1), metadata={"no": 8})],
#     [sg.Button(str(9), key=f"-button9",size=(3, 1), metadata={"no": 9}), sg.Button(str(10), key=f"-button10",size=(3, 1), metadata={"no": 10}), sg.Button(str(11), key=f"-button11",size=(3, 1), metadata={"no": 11}), sg.Button(str(12), key=f"-button12",size=(3, 1), metadata={"no": 12})],
#     [sg.HSeparator()],
#     [sg.Button("Close")]
# ]


# make window
window = sg.Window("Many buttons", layout)

# イベントループ
for event, values in window.event_iter():
    # close button
    if event == "Close":
        break
    
    # -button という文字で始まっているかどうかをチェック
    # -button で始まっていれば True 
    if event.startswith("-button"):
        # アクジョンがあった event のメタデータの取得
        no = window[event].metadata["no"]
        sg.popup(f"You Pushed {no}")
        # ボタンの無効化(押せなくする)
        window[event].update(disabled=True)

各ポップアップ/ダイアログテストGUI

各ポップアップ/ダイアログの動作を確認するもの。

基本的なポップアップダイアログの表示▼

Basic

(1)シンプルなメッセージダイアログ
(2)OKボタン付きのダイアログ
(3)OK/Cancelボタン付きのダイアログ
(4)Yes/Noボタン付きのダイアログ
(5)Cancelボタン付きのダイアログ
(6)エラーメッセージ用のダイアログ
(7)ボタンなしのダイアログ


通知関連のポップアップ▼

Notify

(1)システムの通知領域にメッセージを表示
(2)時間指定で閉じるダイアログ


テキスト入力関連のポップアップ▼

Input Text

(1)テキスト入力ダイアログ
(2)カスタムフォントを使用したテキスト入力ダイアログ
(3)スクロール可能な複数行入力ボックス


ファイル選択関連のポップアップ▼

Select File

(1)ファイル選択ダイアログ
(2)フォルダ選択ダイアログ


カレンダー関連のポップアップ▼

Calendar

日付選択用のカレンダーダイアログ


TkEasyGUI特有の機能▼

Others

(1)色選択ダイアログ
(2)カスタムボタン付きのダイアログ


コードは以下の通りになります。
公式サンプル popup_test.py

import TkEasyGUI as sg


def main():
    while True:
        # select buttons
        group = sg.popup_listbox(
            values=[
                "Basic", # 基本のポップアップグループ
                "Notify", # 通知ポップアップグループ
                "Input Text", # テキスト入力ポップアップグループ
                "Select File", # ファイル選択ポップアップグループ
                "Calendar", # カレンダーポップアップグループ
                "Others", # その他ポップアップグループ
            ],
            message="Please select popup group:")
        if group is None:
            break
        else:
            popup_test(group)

def popup_test(group):
    # 基本的なポップアップダイアログの表示
    if group == "Basic":
        # シンプルなメッセージダイアログ
        sg.popup("[1] popup")
        
        # OKボタン付きのダイアログ
        sg.popup_ok("[2] popup_ok")
        
        # OK/Cancelボタン付きのダイアログ
        # ユーザーの選択を変数に格納
        r = sg.popup_ok_cancel("[3] popup_ok_cancel")
        if r == "Cancel":
            return
        
        # Yes/Noボタン付きのダイアログ
        print(sg.popup_yes_no("[4] popup_yes_no"))
        
        # Cancelボタン付きのダイアログ
        sg.popup_cancel("[5] popup_cancel")
        
        # エラーメッセージ用のダイアログ
        sg.popup_error("[6] popup_error")
        
        # ボタンなしのダイアログ
        sg.popup_no_buttons("[7]popup_no_buttons")
    
    # 通知関連のポップアップ
    if group == "Notify":
        
        # システムの通知領域にメッセージを表示
        sg.popup_notify("[1] popup_notify")
        
       # 3秒後に自動で閉じるダイアログ
        print(sg.popup_auto_close("[2] popup_auto_close - 3sec", auto_close_duration=3))
        
        # 待機せずにダイアログを表示
        # sg.popup_no_wait("[3] popup_no_wait")
    
    # テキスト入力関連のポップアップ
    if group == "Input Text":
        
         # テキスト入力ダイアログ
        print(sg.popup_get_text("[1] popup_get_text"))
        
       # カスタムフォントを使用したテキスト入力ダイアログ
        print(sg.popup_get_text("[2] popup_get_text", font=("Arial", 20)))
        
        # スクロール可能な複数行入力ボックス
        print(sg.popup_scrolled("[3] popup_scrolled"))
        
    # ファイル選択関連のポップアップ
    if group == "Select File":
        # ファイル選択ダイアログ
        print(sg.popup_get_file("[1] popup_get_file"))
        
        # フォルダ選択ダイアログ
        print(sg.popup_get_folder("[2] popup_get_folder"))
    
    # カレンダー関連のポップアップ
    if group == "Calendar":
        # 日付選択用のカレンダーダイアログ
        print(sg.popup_get_date("[1] popup_get_date"))
    
    # その他の特殊なポップアップ(TkEasyGUI特有の機能)
    if group == "Others":
        # --- Below are TkEasyGUI only functionalities ---
        # 色選択ダイアログ
        print(sg.popup_color("[1] popup_color"))
        
        # カスタムボタン付きのダイアログ
        print(sg.popup_buttons("[2] popup_buttons", buttons=["Apple", "Banana", "Orange"]))

if __name__ == "__main__":
    main()

ファイルとフォルダの選択機能テストGUI

「sg.FileBrowse」の左に「sg.Input」があればファイル名が自動で入力される。
左に「sg.Input」がなくとも「sg.FileBrowse」の引数「target_key」を加え、値は「sg.Input」のキーの文字列を書き込む

複数ファイルの選択をすると「;」でパスを区切っています。

ファイルの選択▼

複数ファイルの選択▼

フォルダの選択▼

全てを選択した状態で「OK」を押し表示されたポップアップ

コードは以下の通りになります。
公式サンプル filebrowse_test.py

import TkEasyGUI as sg

# ウィンドウのレイアウトを定義
window = sg.Window("FileBrowser test", layout=[
    [sg.Text(
        "File path:" # ラベル
    )],
    [sg.Input(
            "", # テキスト
            key="-filepath1-" # 要素の参照キー
        ), 
        sg.FileBrowse() # 単一ファイル選択
    ], 
    [sg.Text(
            "Multiple path:" # ラベル
    )],
    [sg.Input(
            "", # テキスト
            key="-filepath2-" # 要素の参照キー
        ),
        sg.FilesBrowse() # 複数ファイルの選択
    ], 
    [sg.Text(
            "Folder path:" # ラベル
    )],
    [sg.Input("",
            key="-folderpath-" # 要素の参照キー
        ), 
        sg.FolderBrowse() # フォルダの選択
    ],
    [sg.Button(
            "OK" # ラベル
    )],
])

# イベントループ
while window.is_alive():
    event, values = window.read()
    print("#", event, values)
    if event == "OK":
        print(values)
        a = [f"path1={values['-filepath1-']}",
            f"path2={values['-filepath2-']}",
            f"folder={values['-folderpath-']}"]
        
        # ポップアップで選択結果を表示
        sg.popup("Selected:\n" + "\n".join(a))
        break
    
window.close()

ブラウズボタンテストGUI

様々なブラウズボタンを試すもの。

コードは以下の通りになります。
公式サンプル listbrowse_test.py

import json

import TkEasyGUI as eg

window = eg.Window(
    "hoge",
    layout=[
        [eg.Text("FileBrowse:")],
        [
            eg.Input(
                "", # テキスト
                expand_x=True, # 水平方向に広げる
                key="-file-result-" # 要素の参照キー
            ),
            eg.FileBrowse(), # 単一ファイル選択
        ],
        [eg.Text("ListBrowse:")],
        [
            eg.Input(
                "", # テキスト
                expand_x=True, # 水平方向に広げる
                key="-list-result-" # 要素の参照キー
            ),
            eg.ListBrowse(values=["red", "green", "blue"]), # リストブラウズ
        ],
        [eg.Text("MultilineBrowse:")],
        [
            eg.Input(
                "", # テキスト
                expand_x=True, # 水平方向に広げる
                key="-text-result-" # 要素の参照キー
            ),
            eg.MultilineBrowse(
                "This is a pen." # テキスト
            )
        ], # 複数行テキスト入力ブラウズ
        [eg.HSeparator()], # 横線
        [eg.Button("OK"), eg.Button("Cancel")],
    ],
)
for event, values in window.event_iter():
    print("#", event, values)
    if event == "OK":
        json_str = json.dumps(values, indent=4, ensure_ascii=False)
        eg.print(json_str)
        break
    elif event == "Cancel":
        break

画像の表示GUI

画像を表示し、「Change」ボタンクリックで画像を変更しています。

コードは以下の通りになります。
公式サンプル image_test.py

from PIL import Image

import TkEasyGUI as sg


def main():
    # 画像のロード
    def_image = Image.open("a.jpg").resize((400, 400))
    
    # ウィンドウのレイアウトを定義
    window = sg.Window("Hello World", layout=[
        [sg.Text(
            "Hello World" # ラベル
        )],
        [sg.Image(
            def_image, # 画像のバイナリデータ
            key="-image-" # 要素の参照キー
        )], # 画像の描写
        [sg.Button(
            "Change" # ラベル
        )],
        [sg.Button(
            "OK" # ラベル
        )]
    ])
    
    # イベントループ
    while True:
        event, _ = window.read()
        if event == "Change":
            # "Change"ボタンが押されたら画像を更新
            window["-image-"].update(filename="b.jpg")
        if event in [sg.WIN_CLOSED, "OK"]:
            break

if __name__ == "__main__":
    main()

アニメーションGUI

背景画像の上に回転する円弧を描画しています。

コードは以下の通りになります。
公式サンプル graph_image.py

from PIL import Image

import TkEasyGUI as sg

# 背景画像の読み込みとリサイズ
back_image = Image.open("b.jpg").resize((400, 400))

def main():
    # ウィンドウのレイアウトを定義
    layout = [
        [sg.Graph(
            canvas_size=(400, 400), # 縦横サイズ
            key='-canvas-', # 要素の参照キー
        )],
        [sg.Button(
            'Close' # ラベル
        )]
    ]
    
    # ウィンドウの作成
    window = sg.Window('graph test', layout)
    canvas: sg.Graph = window['-canvas-']
    
    # イベントループ
    angle = 0
    while True:
        event, _ = window.read(timeout=100)
        if event in [sg.WINDOW_CLOSED, "Close"]:
            break
        
        # 描画処理
        canvas.erase()  # キャンバスをクリア
        canvas.draw_image(data=back_image, location=(0, 0))  # 背景画像を描画
        
        angle1 = angle % 360  # 開始角度(0-359の範囲)
        angle2 = (angle + 90) % 360  # 終了角度(開始角度から90度進んだ角度)
        
        # 動的に色を変更(赤は固定、緑と青は角度に応じて変化)
        color = sg.rgb(100, 206 + (angle * 5 % 50), 156 + ((angle * 10) % 100))
        
        # 円弧を描画
        canvas.draw_arc((100, 100), (300, 300),  # 円弧の外接矩形
                        start_angle=angle1, extent=angle2,  # 開始角度と弧の範囲
                        fill_color=color, arc_color="white")  # 塗りつぶし色と輪郭色
        
        angle += 1  # 角度を1度増加
        window.refresh()  # ウィンドウの表示を更新
        
    window.close()

if __name__ == '__main__':
    main()


カメラテストGUI

カメラを使ってGUIに映像を表示する。

「OpenCV」のインストールの必要があります。

pip install opencv-python

コードは以下の通りになります。
公式サンプル opencv_camera.py

import cv2 as cv

import TkEasyGUI as sg

# カメラ
vc = cv.VideoCapture(0)

# ウィンドウのレイアウトを定義
layout = [
    [sg.Button("Exit")],
    [sg.Image(key="-image-", size=(400, 300))],
]

window = sg.Window("Camera Test", layout)

# イベントループ
while True:
    event, values = window.read(timeout=100)
    print('#', event, values)
    if event in (sg.WIN_CLOSED, "Exit"):
        break
    
    # キャプチャー
    ret, frame = vc.read()
    if ret:
        frame = cv.resize(frame, (400, 300), fx=0, fy=0)
        img = cv.imencode(".png", frame)[1].tobytes()
        window["-image-"].update(img)

vc.release()
window.close()

ウィンドウの手前固定表示

ウィンドウを手前に固定するもの。

コードは以下の通りになります。
公式サンプル keep_on_top.py

import TkEasyGUI as sg

window = sg.Window("Keeo on Top", layout=[
        [
            sg.Button("x"),
            sg.Text("This window will stay on top")
        ],
    ],
    keep_on_top=True, # ウィンドウえを手前に固定
    no_titlebar=True, # GUIタイトルバーの非表示
    grab_anywhere=True, # ウィンドウをどこ触っても移動させれる
    alpha_channel=0.1, # ウィンドウの透明度
)
while True:
    event, values = window.read()
    if event in [sg.WIN_CLOSED, "x"]:
        break
window.close()

キーイベントテストGUI

キー入力の動作確認をするもの。

コードは以下の通りになります。
公式サンプル window_key_events.py

# Window key events
import TkEasyGUI as eg

# ウィンドウのレイアウトを定義
layout = [
    [
        eg.Frame(
            "",
            [
                [eg.Image(size=(300, 300), filename="a.jpg")],
            ],
        ),
        eg.Frame(
            "Description",
            [
                [eg.Text("# Window('', enable_key_events=True)")],
                # [eg.Text("# Window('', return_keyboard_events=True)")],
                [eg.HSeparator()],
                [eg.Text("[S] ... Show message")],
                [eg.Text("[A] ... Show message")],
                [eg.Text("[ESC] ... Close")],
                [eg.Text("[Space] ... Show message")],
            ],
        ),
    ],
]

# ウィンドウの作成
window = eg.Window(
    'Window key test', layout,
    enable_key_events=True, # キー入力の受付を許可する
    # return_keyboard_events=True,
    )

# イベントループ
while True:
    event, values = window.read()
    print(f"[{event}]", values)
    if event == eg.WIN_CLOSED:
        break
    # PySimpleGUI key events --> return_keyboard_events=True
    # Escキーが押された場合
    if event in ['Escape:27', "Escape:889192475"]: # [(for win), (for mac)]
        
        eg.popup("Escape key pressed")
        break
    
    # spaceキーが押された場合
    if event in ['space:20', "space:822083616"]:
        eg.popup("space key pressed")
      
    # s キーが押された場合
    elif event == 's':
        eg.popup("[S] Hello, World!")
    
    # a キーが押された場合
    elif event == 'a':
        eg.popup("[A] Hello, World!")
    
    # TkEasyGUI key events --> enable_key_events=True
    # WindowsPCのキーを押された場合
    if event == eg.WINDOW_KEY_EVENT:
        # 押されたキーの情報を取得
        key = values["key"]
        
        # Escキーが押された場合
        if key == "Escape":
            eg.popup("Escape key pressed")
            break
        # spaceキーが押された場合
        if key == "space":
            eg.popup("space key pressed")
        else: # それ以外のキー
            eg.popup(f"[{key}] Hello, World!")
window.close()

メニューテストGUI

ウィンドウメニューの設定。

コードは以下の通りになります。
公式サンプル menu_test.py

# import PySimpleGUI as sg
import TkEasyGUI as sg

# メニューの定義
menu_def = [
    ['File',['!New', '---', 'Open', 'Save', ['Save As', 'Save Temp'], '---', 'Exit']],
    ['Edit',['Paste','Redo','Undo']],
    ['test1',['Hello', 'World::-world1-']],
    ['test2',['Hello', 'World::-world2-']],
]

# ウィンドウのレイアウトを定義
layout = [
    [sg.Menu(menu_def)], # メニュー
    [sg.Text('This is a test window for Menu element.')],
    [sg.Multiline('', size=(60, 8), key="-log-")],
]
window = sg.Window('Menu test',layout)

# イベントループ
while True:
    event, values = window.read()
    print(event, values)
    if event in [sg.WINDOW_CLOSED, "Exit"]:
        break
    if event == "Hello":
        sg.popup("Hello Popup")
        continue
    # else
    # Multiline にメニューアクションを表示する
    window["-log-"].print(f"{event}", text_color="blue", background_color="yellow")
    window["-log-"].print("---", text_color="red", background_color="lightblue")
window.close()
  • "!label" ラベルの初めに「!」があると、メニューが無効になる

  • "label::-event_name-" ラベルのあとに「::」をつけを入れるとイベント名が設定できる

  • "---" 区切り線


TkEasyGUI用ペイントツール

バインドを設定することなく処理が行える
TkEasyGUI独自のペイントツール

コードは以下の通りになります。
公式サンプル paint_eg.py

"""
Paint tool for TkEasyGUI
Using original event model
"""
import TkEasyGUI as sg

# canvas
canvas = sg.Canvas(
    size=(400, 400), # キャンパスサイズ
    key="-canvas-", # 要素の参照キー
    background_color="red", # 背景色は赤
    enable_events=True # アクションがあれば実行する引数
)
"""
# same as below
canvas = sg.Canvas(
    size=(400, 400),
    key="-canvas-",
    background_color="red",
).bind_events({
    "<ButtonPress>": "mousedown",
    "<ButtonRelease>": "mouseup",
    "<Motion>": "mousemove"
}, "system")
"""

# window create
window = sg.Window("Paint tool", layout=[
    [sg.Button("Exit"), sg.Button("Clear")], # 終了ボタン、クリアボタン
    [canvas]])

# マウスボタンが押されているかのフラグ
flag_on = False

# イベントループ
for event, values in window.event_iter():
    print("#", event, values)
    if event in (None, "Exit"):
        break
    if event == "Clear":
        # キャンパスのクリア
        canvas.clear()
        
    if event == "-canvas-":
        
        # イベントの種類を確認
        event = values["event"]
        event_type = values["event_type"]
        
        # マウスボタンが押された時
        if event_type == "mousedown":
            flag_on = True
        
        # マウスボタンが離された時
        elif event_type == "mouseup":
            flag_on = False
        
        # ウスが動いた時
        elif event_type == "mousemove":
            
            # マウスボタンが押されていない場合は何もしない
            if not flag_on:
                continue
            
            # マウスの現在位置を取得
            x, y = event.x, event.y
            
            # マウスの位置に白い円を描画(直径10ピクセル)
            canvas.create_oval(x, y, x+10, y+10, fill="white", outline="white")

お絵描きGUI

動作は前のものと同じです。

コードは以下の通りになります。
公式サンプル paint_compatible.py

# import PySimpleGUI as sg
import TkEasyGUI as sg

# canvas
canvas = sg.Canvas(
    size=(400, 400), # キャンパスサイズ
    key="-canvas-", # 要素の参照キー
    background_color="red" # 背景色は赤
    )

# window create
window = sg.Window("Hello World", layout=[
    [sg.Button("Exit")], # 終了ボタン
    [canvas],
], finalize=True)

# ユーザーイベントのバインド
# canvas.bind("イベント名", "ハンドル名", イベントの伝播)
# マウスの動き、ボタンを押す、ボタンを離すの3つのイベントを設定
canvas.bind("<Motion>", "motion", True)
canvas.bind("<ButtonPress>", "on", True)
canvas.bind("<ButtonRelease>", "off", True)

# マウスボタンが押されているかのフラグ
flag_on = False

# イベントループ
while True:
    event, values = window.read()
    print("@", event, values)
    if event in (None, "Exit"):
        break
    # マウスボタンが押された時
    if event == "-canvas-on":
        flag_on = True
    
    # マウスボタンが離された時
    elif event == "-canvas-off":
        flag_on = False
        
    # マウスが動いた時
    elif event == "-canvas-motion":
        # マウスボタンが押されていない場合は何もしない
        if not flag_on:
            continue
        # マウスイベントとキャンバスの取得
        e = canvas.user_bind_event
        w = canvas.tk_canvas
        
        # マウスの現在位置を取得
        x, y = e.x, e.y
        
        # マウスの位置に白い円を描画(直径10ピクセル)
        w.create_oval(x, y, x+10, y+10, fill="white", outline="white")

列のテスト

列の動作テストする。

ウィンドウのサイズを変更して「Get Size」ボタンを押すと左のテキストボックス「col3」の数値が変わります。

デフォルトのウィンドウ▼

小さくしたウィンドウ▼

大きくしたウィンドウ▼

コードは以下の通りになります。
公式サンプル column_test.py

# Column test
import TkEasyGUI as eg

# sample text
a_text = (("0123456789" * 5) + "\n") * 5

# サブレイアウトの定義
layout1 = [[eg.Multiline(a_text, # テキスト
                         key="-size-", # 要素の参照キー
                         expand_x=True, # 水平方向に広げる
                         expand_y=True # 垂直方向に広げる
                         )]]
layout2 = [[eg.Multiline(a_text, # テキスト
                         expand_x=True, # 水平方向に広げる
                         expand_y=True # 垂直方向に広げる
                         )]]
layout3 = [
    [eg.Multiline(
        a_text,  # テキスト
        expand_x=True, # 水平方向に広げる
        expand_y=True # 垂直方向に広げる
        )],
    [eg.InputText("Input1"), eg.Button("Button2")]
]

# 列のレイアウト定義
col1 = eg.Column(
    layout1, # レイアウト
    key="col1", # 要素の参照キー
    width=300, # 横幅
    expand_y=True # 垂直方向に広げる
    )
col2 = eg.Column(
    layout2,  # レイアウト
    key="col2", # 要素の参照キー
    width=300, 
    expand_y=True 
)
col3 = eg.Column(
    layout3,  # レイアウト
    key="col3", # 要素の参照キー
    expand_x=True, # 水平方向に広げる
    expand_y=True # 垂直方向に広げる
)

# ウィンドウのレイアウトを定義
main_layout = [
    [col1, col2, col3],
    [eg.HSeparator()], # 横線
    [eg.Button("Get Size"), eg.Button("Close")],
]
# create window
window = eg.Window(
    "Column test",
    main_layout,
    resizable=True, # ウィンドウをリサイズ許可
    size=(900, 600),
    enable_events=True # アクションがあれば実行する引数
)

# イベントのループ
while window.is_running():
    event, values = window.read()
    if event == "Get Size":
        # get_width() でウィンドウの幅を取得
        text = f"col1={col1.get_width()}\ncol2={col2.get_width()}\ncol3={col3.get_width()}"
        window["-size-"].update(text)
    elif event == "Close":
        break
window.close()

キャンバスに図形を描画GUI

キャンパスに図形を描画する。

コードは以下の通りになります。
公式サンプル canvas_test.py

import TkEasyGUI as sg

# import PySimpleGUI as eg
# ウィンドウのレイアウトを定義
layout = [[
    sg.Canvas(
        size=(400, 400), # サイズ
        key="-canvas-", # 要素の参照キー
        background_color="white" # 背景色
    )]]

window = sg.Window("Canvas Test", layout)
canvas = window["-canvas-"]

# イベントループ
painted = False # 描画済みかどうかを示すフラグ
while True:
    event, _ = window.read(timeout=10) # イベントとその値を読み取る
    print(event)
    if event == sg.WINDOW_CLOSED:
        break
    
    # キャンバスに図形を描画(一度だけ)
    if not painted:
        widget = canvas.Widget # キャンバスのTkinterウィジェットを取得
        
        # 図形の描画
        widget.create_rectangle(10, 10, 300, 300, fill="yellow")  # 黄色の長方形
        widget.create_oval(50, 50, 350, 350, fill="blue")         # 青色の楕円
        widget.create_line(10, 10, 390, 390, fill="red", width=5) # 赤色の線
        
        painted = True # 描画完了フラグを設定
window.close()

簡易電卓GUI

簡単な計算機のサンプル

コードは以下の通りになります。
公式サンプル calc.py

import TkEasyGUI as sg

# 電卓のボタンレイアウトを定義
calc_buttons = [
    ["C", "←", "//", "/"],
    ["7", "8", "9", "*"],
    ["4", "5", "6", "-"],
    ["1", "2", "3", "+"],
    ["0", ".", "%", "="]
]

# フォント
font = ("Helvetica", 20)

# ウィンドウのレイアウトを定義
layout = [
    [sg.Input("0",
             key="-output-", # 要素の参照キー
             background_color="white", # 背景色
             color="black", #テキストカラー
             readonly_background_color="white", #読み取り専用時の背景色
             readonly=True, # 読み取り専用
             expand_x=True # 横に詰める
             )],
]
# calc_buttons からボタンを作成
for row in calc_buttons:
    buttons = []
    for ch in row:
        btn = sg.Button(
            ch, # ラベル
            key=f"-btn{ch}", # # 要素の参照キー
            size=(4, 1), # サイズ
        )
        buttons.append(btn)
    layout.append(buttons)

# 最終的なレイアウト
# layout = [
#     [sg.Input("0",
#              key="-output-", 
#              background_color="white",
#              color="black",
#              readonly_background_color="white",
#              readonly=True,
#              expand_x=True)],
#     [sg.Button("C", key="-btnC", size=(4, 1)), sg.Button("←", key="-btn←", size=(4, 1)), sg.Button("//", key="-btn"//",", size=(4, 1)), sg.Button("/", key="-btn"/"],", size=(4, 1))],
#     ~~~
#     [sg.Button("0", key="-btn0", size=(4, 1)), sg.Button(".", key="-btn.", size=(4, 1)), sg.Button("%", key="-btn%", size=(4, 1)), sg.Button("=", key="-btn=", size=(4, 1))],
# ]

window = sg.Window("Calc", layout, font=font)

# イベントループ
output = "0"
for event, values in window.event_iter():
    if event == sg.WINDOW_CLOSED:
        break
    
    # ボタンが押された場合
    # -btn という文字で始まっているかどうかをチェック
    # -btn で始まっていれば True 
    if event.startswith("-btn"):
        # print(event.startswith("-btn"))
        
        # 押されたボタンのラベルを取得
        ch = window[event].get()
        
        # 出力が "0" またはエラーメッセージの場合、クリア
        if output == "0" or output.startswith("E:"):
            output = ""
            
        # ボタンの種類に応じた処理
        if ch == "C": # クリアキー
            output = "0"
        elif ch == "←": # バックスペースキー
            output = output[:-1]
        elif ch == "=": # 計算実行
            try:
                output = str(eval(output))
            except Exception as e:
                output = "E:" + str(e)
        else:
            # その他のキー(数字や演算子)を追加
            output += ch
        # Input テキストのアップデート
        window["-output-"].update(output)

イベントフックのテストGUI

イベントフックは、プログラミングにおいて特定のイベントが発生したときに自動的に実行される関数やコードの断片のことを指します。

イベントフックは、特定のGUIイベント(ボタンクリックなど)が発生したときに、メインのイベントハンドラの前または後に実行される追加の関数です。

起動画面
ファイルの選択をした後

「OK」ボタンを押したとき

「STOP」ボタンを押したとき

コードは以下の通りになります。
公式サンプル event_hooks.py

import TkEasyGUI as eg

# ウィンドウのレイアウトを定義
window = eg.Window("Event Hook Test", layout=[
    [eg.Text("Please Push OK Button, and see console log.")],
    [eg.Input("", key="-filepath-"), eg.FileBrowse()], # ファイル選択用の入力フィールドとボタン
    [eg.Button("OK")],
    [eg.Button("STOP")],
])

# イベントフックの登録
window.register_event_hooks({
    "OK": [
        # OKボタンが押されたときに実行される3つのフック
        lambda event, values: print("hook#1:", event, values),
        lambda event, values: print("hook#2:", event, values),
        lambda event, values: print("hook#3:", event, values),
    ],
    "STOP": [
        # STOPボタンが押されたときに実行される4つのフック
        lambda event, values: print("hook#1:", event, values),
        lambda event, values: print("hook#2:", event, values),
        lambda event, values: True, # イベント伝播を停止
        lambda event, values: print("hook#3:", event, values),# これは実行されない
    ]
})

# イベントループ
while window.is_alive():
    event, values = window.read() # イベントとその値を読み取る
    if event == "OK":
        print("pushed OK")
    elif event == "STOP":
        print("pushed STOP")
    elif event == "STOP-stopped":
        print("stopped OK:", event, values)
        break
window.close()

時計GUI

簡単な時計

コードは以下の通りになります。
公式サンプル clock.py

import datetime

import TkEasyGUI as sg

# ウィンドウのレイアウトを定義
layout = [
    [sg.Text(
        "00:00:00", # ラベル
        key="-output-", # 要素の参照キー
        font=("Helvetica", 80) # フォントタイプ、大きさ
        )
    ]
]
window = sg.Window("Digital Clock", layout)

# イベントループ
while True:
    event, _ = window.read(timeout=10) # イベントとその値を読み取る
    if event == sg.WINDOW_CLOSED:
        break
    
    # 現在の時間の取得
    now = datetime.datetime.now()
    
    # Text のテキストのアップデート
    window["-output-"].update(
        now.strftime("%H:%M:%S")
    )

フォント一覧GUI

リストボックス内のフォント名をクリックすると、一番下のテキストボックスにフォント名が表示されます。

起動画面
フォントの選択

コードは以下の通りになります。
公式サンプル fontlist.py


import TkEasyGUI as sg

# Windowsに登録されているフォント一覧の取得
fontlist = sg.get_font_list()

# ウィンドウのレイアウトを定義
layout = [
    [sg.Listbox( 
        values=fontlist, # 表示するリスト
        size=(40, 20), # 縦横サイズ
        key="-fontlist-", # 要素の参照キー
        enable_events=True, # アクションがあれば実行する引数
    )],
    [sg.Input(
        "-", # テキスト
        key="-font-", # 要素の参照キー
        expand_x=True # 垂直方向に広げる
    )],
]

window = sg.Window("Font List", layout, font=("Arial", 14))

# イベントループ
while True:
    event, values = window.read()
    print("# event:", event, values)
    if event == sg.WINDOW_CLOSED:
        break
    if event == "-fontlist-":
        if values["-fontlist-"]:
            window["-font-"].update(values["-fontlist-"][0])
window.close()


インチ センチ変換GUI

(1)数字の入力バージョン
(2)数字の未入力バージョン

コードは以下の通りになります。
公式サンプル nokey.py

import TkEasyGUI as sg

window = sg.Window("Inch to cm", layout=[
    [sg.Text("Please input Inch:")],
    [sg.Input("1"), sg.Button("Convert")],
])
while True:
    event, values = window.read()
    # print(values)
    if event == sg.WIN_CLOSED:
        break
    if event == "Convert":
        try:
            inch = float(values[0]) # key = 0
            cm = inch * 2.54
            result = f"{inch} inch is {cm} cm"
            # 結果をポップアップで表示
            sg.popup(result)
        except Exception as _e:
            # エラーをポップアップで表示
            sg.popup("Please input number.")
window.close()

キーを設定なしで処理をしている。
キーが設定されていなければ「{'event_type': 'command', 0: '1'}」自動でバーが割り当てられる。


メモ帳GUI

「Save」をするとテキストファイルが保存されます。
GUIを閉じて「Open」をクリックすつと前回書いてあったものが表示されます。

コードは以下の通りになります。
公式サンプル notepad.py

import os

import TkEasyGUI as eg

# set path
SCRIPT_DIR = os.path.dirname(__file__) # 実行ファイルのパスを取得
SAVE_FILE = os.path.join(SCRIPT_DIR, "notepad-save-data.txt")

# ウィンドウのレイアウトを定義
layout = [
    [eg.Multiline(size=(40, 15), key="text")],
    [eg.Button("Save"), eg.Button("Open"), eg.Button("Exit")],
]
window = eg.Window("Notepad", layout=layout)

# イベントループ
while True:
    event, values = window.read()
    if event in ["Exit", eg.WINDOW_CLOSED]: # exit button
        break
    # save button
    if event == "Save":
        # notepad-save-data.txt に書き込む
        with open(SAVE_FILE, "w", encoding="utf-8") as f:
            f.write(values["text"])
        eg.popup("Saved")
    # open button
    if event == "Open":
        # notepad-save-data.txt が存在しているか
        if not os.path.exists(SAVE_FILE):
            continue
        
        # notepad-save-data.txt を読み込む
        with open(SAVE_FILE, "r", encoding="utf-8") as f:
            text = f.read()
        # 読み込んだテキストにアップデート
        window["text"].update(text)
window.close()

ポップアップテストGUI

ボタンを押すとポップアップが出るもの。

コードは以下の通りになります。
公式サンプル simple_dialog.py

import TkEasyGUI as eg

# ウィンドウのレイアウトを定義
layout = [
    [eg.Text("For wisdom is better than corals;\n" + 
             "All other desirable things cannot compare to it.")],
    [eg.Button("OK")]
]

window = eg.Window("Proverb", layout=layout)

# イベントループ
while True:
    # イベントとその値を読み取る
    event, values = window.read()
    # close button
    if event == eg.WINDOW_CLOSED:
        break
    
    # OKボタンがクリックされた場合
    if event == "OK":
        # ポップアップメッセージを表示
        eg.popup("Pushed OK Button")
        break
window.close()

ポップアップのカスタム

ポップアップに任意のラベルと返値を設定できる

コードは以下の通りになります。
公式サンプル localize_test.py

import TkEasyGUI as eg

# --- popup_yes_no は、自動でローカライズに対応している ---

# YES or NO ボタンのあるポップアップ
ans = eg.popup_yes_no("英語が話せますか?")
eg.print("答えは? [", ans, "].\npopup_yes_noを使うと、OS言語が何語でも答えは[Yes]か[No]になります。")

# カスタマイズの例
ans = eg.popup_yes_no(
    "英語がは話せますか?",
    yes_label="話せる", no_label="話せない", # ボタンの文字
    yes_value="可", no_value="不可") # ボタンの返値
eg.print("答えは... [", ans, "]. 戻り値もカスタマイズできます。")

バージョン情報GUI

現在使っているPythonのライブラリ、システム情報などを取得するGUI

コードは以下の通りになります。
公式サンプル version_info.py

import subprocess

import TkEasyGUI as eg

WEB_SITE = "https://github.com/kujirahand/tkeasygui-python/"

# 国際化対応のためのテキスト取得
S_COPY = eg.get_text("Copy")
S_CLOSE = eg.get_text("Close")

# ウィンドウのレイアウトを定義
layout = [
    # TkEasyGUIのドキュメント文字列を表示
    [eg.Text(f"{eg.__doc__.strip()}", color="navy")],
    [
        # バージョン情報を表示するフレーム
        eg.Frame(
            "Version", # ラベル
            expand_x=True, # 水平方向に広げる
            layout=[
                [
                    eg.Text("TkEasyGUI:"), # ラベル
                    eg.Button(f"v{eg.__version__}", key="-b1-"),
                    eg.Button("Web"),
                ],
            ],
        )
    ],
    [
         # システム情報を表示するフレーム
        eg.Frame(
            "System info:", # ラベル
            layout=[
                [
                    eg.Multiline(
                        f"{eg.get_system_info()}",
                        key="-sys-info-", # 要素の参照キー
                        size=(60, 5), # 縦横サイズ
                        expand_x=True, # 水平方向に広げる
                    )
                ],
                [eg.Button(S_COPY), eg.Button("Copy as Markdown")],
            ],
        )
    ],
    [
        # 右揃えのOKとCloseボタンを配置
        eg.Column(
            layout=[[eg.Button("OK"), eg.Button(S_CLOSE)]],
            text_align="right", # 右後揃え
            expand_x=True, # 水平方向に広げる
        ),
    ],
]
# ウィンドウの作成
window = eg.Window("Version info", layout=layout, font=("", 14 if eg.is_mac() else 9), row_padding=5)

# イベントループ
for event, values in window.event_iter():
    print("#", event, values)
    if event == "OK":
        eg.popup("Thank you.")
        break
    if event == S_CLOSE:
        break
    
    # バージョンボタンがクリックされた場合
    if event in ["-b1-", "-b2-", "-b3-"]:
        btn: eg.Button = window[event]
        # クリックしたボタンの値を取得
        label = btn.get_text()
        
        # クリップボードにコピー
        eg.set_clipboard(label)
        
        # コピーした意図を伝えるポップアップ
        eg.popup(f"Copied to clipboard:\n{label}")
        
    # `Copy` ボタンがクリックされた場合
    if event == S_COPY:
        # Multiline(-sys-info- キー)のテキストを取得
        text = window["-sys-info-"].get_text()
        
        # クリップボードにコピー
        eg.set_clipboard(text)
        
        # コピーした意図を伝えるポップアップ
        eg.popup("Copied to clipboard.")
    
    # `Copy as Markdown` ボタンがクリックされた場合
    if event == "Copy as Markdown":
        
        # Multiline(-sys-info- キー)のテキストを取得
        text = window["-sys-info-"].get_text()
        text = f"```\n{text}\n```\n"
        
        # クリップボードにコピー
        eg.set_clipboard(text)
        
        # コピーした意図を伝えるポップアップ
        eg.popup("Copied markdown to clipboard.")
    
    # `Web`ボタンがクリックされた場合
    if event == "Web":
        if eg.is_mac():
            # Macの場合
            subprocess.call(f"open {WEB_SITE}", shell=True)
        else:
            # Windowsの場合
            subprocess.call(f"start {WEB_SITE}", shell=True)

ライフゲームGUI

「ライフゲーム」というセルオートマトンの一種をシミュレートします。
詳しくは「ライフゲーム」と検索してください。

コードは以下の通りになります。
公式サンプル lifegame.py

# import PySimpleGUI as sg
import random
import time

import TkEasyGUI as sg

# ライフゲームのセルの状態を定義
DEAD = 0  # 死んでいるセル
ALIVE = 1  # 生きているセル

# ゲーム盤の行と列の数を定義
ROWS = 30  # 行数
COLS = 40  # 列数

# ライフゲームのルールに基づいて次の世代の盤面を計算する関数
def calculate_next_generation(board):
    new_board = [[DEAD for _ in range(COLS)] for _ in range(ROWS)]  # 新しい盤面を初期化
    for i in range(ROWS):
        for j in range(COLS):
            neighbors = count_neighbors(board, i, j)  # 周囲の生存セルの数をカウント
            if board[i][j] == ALIVE:  # 現在のセルが生きている場合
                if neighbors < 2 or neighbors > 3:  # 過疎または過密で死亡
                    new_board[i][j] = DEAD
                else:  # 生存
                    new_board[i][j] = ALIVE
            else:  # 現在のセルが死んでいる場合
                if neighbors == 3:  # ちょうど3つの生存セルに囲まれている場合、誕生
                    new_board[i][j] = ALIVE
    return new_board  # 新しい盤面を返す

# 指定されたセルの周囲の生存セルの数を数える関数
def count_neighbors(board, x, y):
    count = 0  # カウントを初期化
    for i in range(-1, 2):  # 隣接するセルをチェック
        for j in range(-1, 2):
            if i == 0 and j == 0:  # 自分自身はスキップ
                continue
            if 0 <= x + i < ROWS and 0 <= y + j < COLS:  # 境界チェック
                count += board[x + i][y + j]  # 生存セルをカウント
    return count  # 生存セルの数を返す

# GUIを作成する関数
def create_gui(board):
    layout = []  # レイアウトを初期化
    for i in range(ROWS):
        row = []  # 行を初期化
        for j in range(COLS):
            row.append(sg.Text("", key=(i, j), size=(2, 1), pad=(0,0), enable_events=True))  # セルを追加
        layout.append(row)  # 行をレイアウトに追加

    window = sg.Window('Life Game', layout, row_padding=0, finalize=True)  # ウィンドウを作成
    return window  # ウィンドウを返す

# GUI上の盤面を更新する関数
def update_gui(window, board):
    for i in range(ROWS):
        for j in range(COLS):
            bg = "red" if board[i][j] == ALIVE else "black"  # セルの状態に応じて色を決定
            window[(i, j)].update(background_color=bg)  # セルの色を更新

# メイン関数
def main():
    # ゲーム盤の初期化
    board = [[random.choice([ALIVE, DEAD]) for _ in range(COLS)] for _ in range(ROWS)]  # ランダムに初期化
    window = create_gui(board)  # GUIを作成
    paused = False  # 一時停止状態を初期化
    while True:
        event, _ = window.read(timeout=100 if not paused else None)  # イベントを読み込む
        if event == sg.WINDOW_CLOSED:  # ウィンドウが閉じられた場合
            break
        elif event != sg.TIMEOUT_KEY:  # セルがクリックされた場合
            x, y = event
            board[x][y] = DEAD if board[x][y] == ALIVE else ALIVE  # セルの状態を反転
            update_gui(window, board)  # GUIを更新
        else:
            board = calculate_next_generation(board)  # 次の世代を計算
            update_gui(window, board)  # GUIを更新
            time.sleep(0.1)  # 少し待つ
    window.close()  # ウィンドウを閉じる

if __name__ == "__main__":
    main()

さいご

初めにドキュメントを読むのもいいですが、サンプルコードを動かしたり、コードを改変して動作を見ることで、使い方がわかってくるのでサンプルコードを動かしてみましょう。


各ウィジェットについてのドキュメント


この記事が気に入ったらサポートをしてみませんか?