イベント

前回、Tk()でメインウィンドウを生成し、ボタンやラベル等の様々なウィジェットを配置してみました。今回はボタンをクリックする等の操作をした際に処理を行う方法について解説します。
※ 2022/10/17 解説コードのウィジェットについて当初ttkを使用していましたがデフォルトに変更しました。ttkについてはこちらで解説しています。

イベントとイベントハンドラ

GUIアプリケーションは「マウスでボタンをクリックした」「入力値が変わった」「マウスが動いた」といった操作を画面上で行いますが、こういった画面上で行われた「何らかの出来事」をイベントと呼びます。

イベントにはそれ以外にも以下のようなものがあります。

  • キーボードのキーが押されたされた
  • 画面がスクロールされた
  • 画面サイズが変更された
  • 画面が閉じられた

つまり、マウスやキーボードを使って画面上で行われた操作はすべてイベントと呼ぶことができます。

また、あるボタンをクリックしたとき等、特定のイベントが発生した際に処理を実装することになりますが、こういった「特定のイベントが発生した時に実行するよう定められた処理」のことをイベントハンドラやイベント処理などと呼びます。また、イベントとイベントハンドラを紐づけることをバインドと呼びます。

通常、GUIアプリケーションはイベントの発生を待ち受けイベント発生が通知されると必要に応じてイベントハンドラが起動します。このイベント発生の待受状態をメインループやイベントループと呼びます。

イベントハンドラの実装

それでは実際に実装してみましょう。ボタンがクリックされた際に文字列をprintするだけの関数、my_funcを実行してみます。

from tkinter import Tk, Button

# メインウィンドウ生成
root = Tk()

# メインウィンドウの設定
root.title("サンプル")


# イベントハンドラ
def my_func(event):
    print("処理実行")
    print(event)


# ボタンウィジェットをメインウィンドウに配置、イベントを登録
button = Button(root, text='button1')
button.bind("<Button-1>", my_func)
button.pack()

# イベントループの開始
root.mainloop()


11行目以降、ボタンがクリックされた際に実行する関数my_funcを定義しています。printで文字列を出力するだけの関数で、引数にイベントの状態が格納されたeventオブジェクトを受け取ります。

19行目ではボタンがクリックされたイベントと、my_funcを紐づけ、つまりバインドしています。バインドは以下のように記述します。

バインド
ウィジェット.bind(イベント名, イベントハンドラ)

イベントは文字列で指定します。ボタンを押下する場合は「<Button-1>」を指定します。ウィジェット生成時にイベントハンドラを登録する方法もあるのですが、本講座ではすべてbindを使用して解説します。

実行してボタンを押下してみると以下のような文字列が出力されます。

処理実行
<ButtonPress event num=1 x=55 y=11>

イベントオブジェクトに座標などのイベントの状態が格納されていることが確認できます。

画面の状態を変更

当然、イベントハンドラでウィジェット等の画面の状態を変更することも可能です。ボタンをクリックするとラベルの文字列が変更されるアプリを作成してみましょう。

StringVar

その前に準備としてにStringVarについて解説します。ラベルを生成する際に引数で表示する文字列を指定しますが、文字列はイミュータブルであるため、後から変更することができません。

StringVarを使用するとsetメソッドで後から文字列を変更することが可能です。

text = StringVar()
text.set("文字列設定")
label = Label(root, textvariable=text)

# setで後からでもラベルの文字列が変更できる
text.set("文字列変更")

ボタンをクリックするとラベルが変更

それではボタンをクリックするとラベルの文字列が変更されるアプリを作成してみましょう。

from tkinter import Tk, StringVar, Button, Label

# メインウィンドウ生成
root = Tk()

# メインウィンドウの設定
root.title("サンプル")

# ラベルの配置
text = StringVar()
text.set("処理はまだ実行されていません")
label = Label(root, textvariable=text)
label.pack()


# イベントハンドラ
def my_func(event):
    global text
    text.set("処理が実行されました")


# ボタンウィジェットをメインウィンドウに配置、イベントを登録
button = Button(root, text='button1')
button.bind("<Button-1>", my_func)
button.pack()

# イベントループの開始
root.mainloop()

変更するラベルはモジュール変数として定義しているためイベントハンドラ内でglobal文を使用します。実行すると以下のようにボタンをクリックするとラベルが変更されることが確認できます。