【Tkinter】ボタンのコールバック関数が即時実行される問題の対処法

PythonのTkinterライブラリでGUIアプリケーションを作成しました。

その際にボタンの表示と同時にコールバック関数が実行されてしまう問題に直面し、解決に手間取ったので、自分用のメモとしてここに記しておきます。

Pythonにおける関数の定義方法

Pythonでは関数を定義する方法が主に2つあります。1つはdefを使う方法、もう1つはlambda式を使う方法です。

# defを使った関数定義
def add(x, y):
    return x + y

print(add(1, 2))  # 出力: 3
# lambdaを使った関数定義
add = lambda x, y: x + y

print(add(1, 2))  # 出力: 3

どちらの方法でも、関数は定義した時点では実行されず、呼び出されたときに初めて実行されます。

TkinterのButtonウィジェット

ボタンがクリックされたときに特定の処理を実行するには、command引数に関数を指定します。例えば、以下のコードでは、ボタンがクリックされるとcallback_func関数が実行されます。

import tkinter as tk

root = tk.Tk()

def callback_func():
    print("Hello Python")

tk.Button(
    text="ボタンテキスト",
    command=callback_func  # コールバック関数を指定
).pack()

root.mainloop()

ここで大切なのは、command引数に渡すのは「関数の実行結果」ではなく「関数オブジェクト」そのものだということです。そのため、ボタンがクリックされるまで関数は実行されません。

関数が即時実行される問題

ところが、引数付きの関数をcommand引数に指定する際、括弧を付けると、その場で関数が実行されてしまいます。

def callback_func(x, y):
    print(x + y)

tk.Button(
    text="ボタンテキスト",
    command=callback_func(1, 2)  # ボタン作成時に関数が実行される
).pack()

この問題を解決するためには、command引数の中でlambda式を使い、関数を再定義することで、ボタンがクリックされたタイミングで関数が呼び出されるようにできます。なぜなら、定義時には関数の中身は実行されないからです。

tk.Button(
  text="ボタンテキスト",
  command=lambda: callback_func(1, 2) # lambdaを使って関数を遅延実行
).pack()

これで、ボタンがクリックされたときに、引数付きのコールバック関数が正しく動作するようになります。

Happy Hacking!