Tkinterの使い方:イベント処理を行う

イベント処理解説ページのアイキャッチ

このページでは Tkinter でのイベントの使い方について解説していきたいと思います。

イベントとは

まずイベントとは、そのアプリで発生した出来事のことを言います。

イベントの例としては下記のようなものが挙げられます。

  • マウスがクリックされた
  • マウスが移動した
  • アプリ内のボタンが押された
  • キーボードの上キーが押された
  • ウィンドウサイズが変更された

特に GUI アプリで扱われるイベントは「ユーザーの操作によって発生する出来事」が多いです。

イベントを使いこなせば、ユーザーから様々な操作を受け付け、それに応じた処理をや機能をユーザーに提供する GUI アプリを作成できるようになります!

イベント処理とは

これらのイベントが発生したことを検知し、その際に特定の処理(関数やメソッド)を実行することを「イベント処理」と言います。

イベント処理のとしては例えば下記のようなものが挙げられます。

  • 「ボタンウィジェットがクリックされた」時に「ラベルに表示する文字列を変更する処理」を実行する
  • 「メインウィンドウのサイズが変更された」時に「そのサイズに応じて表示している文字列のサイズを変更する処理」を実行する

ユーザーからの操作の受け付けはこのイベント処理により実現されますので、特にユーザーからの操作により処理や機能を実行する GUI アプリにおいてはこのイベント処理が重要です。

スポンサーリンク

Tkinter でのイベント処理の設定

Tkinter では下記の3つを後述する bind メソッドにより設定することで、イベント処理を行うことが可能になります。

  • どのウィジェット」で
  • どのイベント」が発生した時に
  • どの処理(関数・メソッド)」を実行するか

例えば下記のように設定を行います。

  • 「ラベルウィジェットA」で「マウスボタンのクリック」が発生した時に「label_click 関数」を実行する
  • 「全てのラベルウィジェット」で「マウスボタンのクリック」が発生した時に「label_class_click 関数」を実行する
  • 「全てのウィジェット」で「サイズ変更」が発生した時に「resize_all_widgets 関数」を実行する

イベント処理の設定は複数行うことも可能です。

例えば1つ目のイベント処理のみ設定した場合、「ラベルウィジェットA」がクリックされた場合は label_click 関数が実行されます。

イベント処理時に実行する関数には、自作した関数を指定することができます。

ですので、例えば上記の例であれば、label_click 関数に「ラベルウィジェットA」がクリックされたときに行いたい処理を記述しておけば、ユーザーがラベルウィジェットAをクリックした時に、自動的にその処理を実行されるようにすることが可能になります。

対象ウィジェットの設定

Tkinter では「どのウィジェット」に対するイベントでイベント処理を実行するかを設定できます。

この設定方法には下記の3種類があり、設定時に使用するメソッドが異なります。

  • 1つのウィジェットにイベント処理を設定する
  • 同じ種類のウィジェットにイベント処理を設定する
  • 全ウィジェットにイベント処理を設定する

1つ1つ解説していきます。

1つのウィジェットにイベント処理を設定する(bind

1つのウィジェットにのみイベント処理を設定するには bind メソッドを使用します。

bindの定義
bind(self, sequence=None, func=None, add=None)

bind というのは「何かと何かを結び付ける」という意味のある英単語です。

特にイベント処理設定に用いる bind は「イベント」と「処理(関数)」とを結び付けを行います。

この結び付けを行っておけば、その「イベント」が発生した時結び付けられている「処理」を Tkinter 本体が自動的に実行してくれるようになります。

引数では後述する下記の3つを指定することができます(add に関してはこのページでは取り扱いません)。

  • sequence:「どのイベント」が発生した時に
  • func:「どの処理(関数・メソッド)」を実行する
  • add:同じウィジェット・イベントに対して追加でイベント処理を設定するかどうか

そして、bind メソッドを実行したインスタンスに対してのみ、イベント処理が設定されます。

つまり、bind メソッドを使用した場合、「どのウィジェット」にイベント処理を設定するかは、bind メソッドをどのウィジェット(のインスタンス)に実行させるかで指定することができます。

例えば下記のスクリプトを実行すれば、label1 に対してのみイベント処理を設定することができます。

bindの使用例
# -*- coding:utf-8 -*-
import tkinter

# イベント発生時に実行する処理
def bg_blue(event):
	global app
	app.config(bg="blue")

def bg_white(event):
	global app
	app.config(bg="white")

# ウィジェットの作成と配置
app = tkinter.Tk()
app.geometry("600x400")
app.config(bg="white")

label1 = tkinter.Label(
	app,
	width=10,
	height=2,
	text="ラベル1"
)
label1.pack(pady=20)

label2 = tkinter.Label(
	app,
	width=10,
	height=2,
	text="ラベル2"
)
label2.pack(pady=20)

button1 = tkinter.Button(
	app,
	width=10,
	height=2,
	text="ボタン1"
)
button1.pack(pady=20)

# イベント処理の設定
label1.bind("<Enter>", bg_blue)
label1.bind("<Leave>", bg_white)

# メインループ
app.mainloop()

bind の引数や引数で指定する関数については後述で説明しますが、ここではイベント処理を設定したウィジェット上にマウスカーソルが入った時に bg_red 関数を、マウスカーソルが離れた時に bg_white 関数をそれぞれ実行するように設定しています。

bg_blue 関数ではアプリの背景の色を青色にする処理を、bg_white 関数ではアプリの背景の色を白色にする処理を記述していますので、マウスカーソルを label1 の上に入れたり離したりすると、アプリの背景が青や白に変わります。

bind使用例

一方で、label2 のラベルや button1 のボタンの上にマウスカーソルを移動させてもアプリの色は変わりません(bg_bluebg_white 関数が実行されない)。

これは label1 に対してのみイベント処理を設定して、label2button1 にはイベント処理を設定していないためです(label2button1bind メソッドを実行していない)。

スポンサーリンク

同じ種類のウィジェットにイベント処理を設定する(bind_class

bind メソッドが1つのウィジェットにイベント処理を設定するのに対し、bind_class メソッドでは同じクラスの全ウィジェット(つまり同じ種類のウィジェット)に対してイベント処理を設定することができます。

bind_classの定義
bind_class(self, className, sequence=None, func=None, add=None)

bind_class メソッド実行時に、className に指定したクラスのウィジェット全てに対してイベント処理を設定することができます。

つまり、bind_class メソッドを使用した場合、「どのウィジェット」にイベント処理を設定するかは、bind_class メソッドの引数 className で指定することができます。

bind_class メソッドを実行するのはどのウィジェットのインスタンスでも良く、引数 className で指定したクラスのウィジェットに対してイベント処理が設定されます。

例えば1つのウィジェットにイベント処理を設定する(bindで紹介したスクリプトの bind 実行部分を下記のように変更すれば、Label クラスのインスタンスである label1label2 に対してイベント処理を設定することができます。

bind_classの使用例
# appはメインウィンドウのインスタンス
# イベント処理の設定
app.bind_class("Label", "<Enter>", bg_blue)
app.bind_class("Label", "<Leave>", bg_white)

今度は label1label2 で、マウスカーソルを合わせたり離したりすることでアプリの背景の色が変わることが確認できると思います。

bind メソッドだとウィジェット1つにしかイベント処理が設定できなかったのに対し、bind_class だと複数の同じ種類のウィジェットに一度にイベント処理が設定できているところがポイントです。

一方で、button1 のボタンの上にマウスカーソルを移動させてもアプリの色は変わりません(bg_bluebg_white 関数が実行されない)。

これは Label クラスに対してのみイベント処理を設定して、Button クラスにはイベント処理を設定していないためです(bind_class の引数に Button を指定していない)。

全ウィジェットにイベント処理を設定する(bind_all

アプリ内の全ウィジェットに対して一括してイベント処理を設定することも可能です。

この場合にはイベント処理設定時に bind_allメソッドを使用します。

bind_allの定義
bind_all(self, sequence=None, func=None, add=None)

bind_all メソッド実行すればアプリ内のウィジェット全てに対してイベント処理を設定することができます。

bind_all メソッドを実行するのはどのウィジェットのインスタンスでも良く、アプリ内の全ウィジェットに対してイベント処理が設定されます。

例えば1つのウィジェットにイベント処理を設定する(bindで紹介したスクリプトの bind 実行部分を下記のように変更すれば、全てのウィジェットに対してイベント処理を設定することができます。

bind_allの使用例
# appはメインウィンドウのインスタンス
# イベント処理の設定
app.bind_all("<Enter>", bg_blue)
app.bind_all("<Leave>", bg_white)

受け付けるイベントの設定

ここまでは主に「どのウィジェット」にイベント処理を設定するかについて解説してきましたが、次は「どのイベント」が発生した時にイベント処理を行うかについて解説していきたいます。

イベントシーケンスの設定

どのイベント」が発生した時にイベント処理を行うかは、bind メソッドの引数により指定します(複数のウィジェットにイベント処理を設定する場合は bind_classbind_all を実行しますが、これらでも同様に引数を指定します)。

下記は bind メソッドの定義の再掲です。

bindの定義
bind(self, sequence=None, func=None, add=None)

「どのイベント」が発生した時にイベント処理を行うかは、引数 sequence により指定します。この引数 sequence に指定する情報を「イベントシーケンス」と呼びます。

このイベントシーケンスは下記の形式で表される文字列になります。

イベントシーケンス
"<[modifier-]event_type[-detail]>"

[modifier-][-detail] は省略可能です。

event_typedetail 、modifierにはそれぞれ下記を指定します。

  • event_type:イベント処理するイベントの種類
    • キーボードのキー押し下げ(KeyPress)、マウスのボタンの押し下げ(ButtonPress)等
  • detail:イベントの種類の詳細
    • event_typeがキーボードやマウスに関する種類である時、「どの」キー or ボタンに対してイベント処理を行うかを設定(-a-b-c-1-2 など)
  • modifier:イベント処理する条件
    • 例えばシフトキーを押された状態でイベントが発生した場合のみイベント処理を行う(Shift-)、マウスがダブルクリックされた時のみイベント処理を行う(Double-)など

例えば bind 実行時にイベントシーケンスを下記のように指定した場合、キーボードのどのキーを押しても button_func が実行されます。

これはイベントシーケンスに event_type のみを指定した例になります。

event_typeの例
# appはメインウィンドウのインスタンス
app.bind("<KeyPress>", func)

下記のようにイベントシーケンスを指定した場合はキーボードの “x” キーを押した時のみ button_func が実行されます。

これは detail を指定した例になります。

detailの例
# appはメインウィンドウのインスタンス
app.bind("<KeyPress-x>", func)

さらに下記のようにイベントシーケンスを指定した場合、キーボードの “Controlキー” を押しながら “x” キーが押された時のみ button_func が実行されるようになります。

これは modifier を指定した例になります。

modifierの例
# appはメインウィンドウのインスタンス
app.bind("<Control-KeyPress-x>", func)

こんな感じでイベントシーケンスに event_type を指定してイベント処理を行いたいイベントの種類を設定することができ、さらに detailmodifier の指定により「どのイベント」が発生した時にイベント処理を行うかを「細かく」指定することが可能です。

MEMO

ただし modifier の設定は私の環境であまり上手く反映されませんでした..

例えば "<Shift-KeyPress-x>" と設定して、Shift キーを押しながら x キーを押してもイベント処理が行われなかったり…

"<Shift-ButtonPress-2>" と設定した場合は、Shift キーを押しながらマウスボタンクリックするとイベント処理が上手く動作したりでちょっと謎でした…

環境によって?は上手く動作しない場合もあるようなので注意してください

modifier は下記のように2つ設定することも可能です。

2つのmodifierの設定
# appはメインウィンドウのインスタンス
app.bind("<Control-Shift-KeyPress-x>", func)

スポンサーリンク

event_type

イベントシーケンスに指定可能な event_type 一覧は下記のようになります(実行する環境によって異なる場合がありますので注意してください)。

  • KeyPress:フォーカスされた状態でキーボードのキーが押された
  • KeyRelease:フォーカスされた状態でキーボードのキーが離された
  • ButtonPress:マウスのボタンが押された
  • ButtonRelease:マウスのボタンが離された
  • Motion:マウスが移動した
  • Enter:マウスがウィジェット上に入った
  • Leave:マウスがウィジェットから離れた
  • FocusIn:ウィジェットにフォーカスが合わされた
  • FocusOut:ウィジェットからフォーカスが離れた
  • Expose:画面に表示された(レイアウト変更の反映なども)
  • Visibility:画面に表示された
  • Create:??
  • Destroy:ウィジェットが終了した
  • Unmap:ウィジェットの配置が取り消された
  • Map:ウィジェットが配置された
  • MapRequest:??
  • Reparent:??
  • Configure:ウィジェットの設定が変更された
  • ConfigureRequest:??
  • Gravity:??
  • ResizeRequest:??
  • Circulate:??
  • Property:??
  • Colormap:??
  • Activate:ウィジェットが有効化された
  • Deactivate::ウィジェットが無効化された
  • MouseWheel:マウスホイール操作が行われた

いつイベントが発生するのか分からないイベントの種類は「??」と記載させていただいています…。分かり次第追記したいと思います。

modifier

イベントシーケンスに指定可能な modifier 一覧は下記のようになります(実行する環境によって異なる場合がありますので注意してください)。

  • Double:2回連続で event_type のイベントが発生した場合にイベント処理を実行
  • Triple:3回連続で event_type のイベントが発生した場合にイベント処理を実行
  • Shift:Shift キー押した状態で event_type のイベントが発生した場合にイベント処理を実行
  • Control:Control キー押した状態で event_type のイベントが発生した場合にイベント処理を実行
  • Alt:Alt キー押した状態で event_type のイベントが発生した場合にイベント処理を実行
  • Lock:Caps Lock 状態で event_type のイベントが発生した場合にイベント処理を実行
  • Any:??

detail

イベントシーケンスに指定可能な detail について説明します。

キーボードのキーに対しては、基本的にキー名がこの detail に指定可能です。

例えば x キーであれば -x と指定します。

マウスのボタンに対しては、数字を指定します。各ボタンに対して数字(1、2、…)が割当てられており、数字に対応したボタンに対してイベント受け付けを設定することができます。

例えば左ボタンであれば -1、右ボタンであれば -2 と指定すれば良いはずです(マウス持ってないので自信ないです…)。

各キーやマウスボタンに対して detail に何を設定すべきかは簡単に調べることも可能です。

下記ページでその方法について解説していますので、detail に何を設定すれば良いか分からない場合は参考にしてみてください。

イベントの調べ方の解説ページのアイキャッチTkinter の使い方:利用可能なイベントやイベントが発生するタイミングを調べる

スポンサーリンク

実行する処理の設定

次はイベント処理時に「どの処理」を行うかの設定について解説していきたいます。

イベント処理時に実行する処理は関数として定義し、bind メソッド実行時(bind_class や bind_all でも同様)にその関数名(もしくはメソッド名)を指定します。

イベント処理時に実行する関数の設定
ウィジェットのインスタンス.bind(イベントシーケンス, 関数名)

このようにイベント発生時に行う処理を定義した関数・メソッドを「イベントハンドラ」と呼びます。ここからは単に「関数・メソッド」ではなく、イベントハンドラと呼びます。

イベントハンドラの定義

イベントハンドラには tkinter.Event のインスタンスが引数として渡されます。

ですので、イベントハンドラには、tkinter.Event のインスタンスが受け取れるように引数 event を指定します。

イベントハンドラ(関数)
def 関数名(event):

クラスのメソッドとして定義する場合は下記のように定義すれば良いです。

イベントハンドラ(メソッド)
def 関数名(self, event):

tkinter.Event クラス

イベントハンドラの引数として渡される tkinter.Event のインスタンスは下記のプロパティを持っています。

  • char:キーを押して入力される文字
  • delta:マウスホイールの動いた量?
  • height:サイズ変更後の高さ
  • keycode:キーを表す数字
  • keysym:キーのイベント detail 名
  • keysym_numkeysym の数字バージョン
  • num:マウスボタンに対応した数字
  • send_event:??
  • serial:??
  • state:イベント発生時の状態を表す数字(Control キーが押されていた場合は “4”、など)
  • time:イベント発生時の時間(絶対時刻ではない?)
  • type:イベントハンドラを実行する引き金になったイベントの種類
  • widget:イベントを受け付けたウィジェット
  • width:サイズ変更後の幅
  • x:ウィジェット上の座標 x
  • x_root:画面上の座標 x
  • y:ウィジェット上の座標 y
  • y_root:画面上の座標 y

これらのプロパティにより、イベント処理時を発生させたイベントがどのようなイベントであるかを調べることが可能です。

例えば下記のようにイベント処理の設定を行い、

イベントのプロパティの確認
# appはメインウィンドウのインスタンス
app.bind("<KeyPress>", func)

アプリ起動後に “n” キーを押せば、この引数 event のプロパティに下記が設定された状態で func 関数が実行され、どのキーが押されたかを確認することができます。

  • char'n'
  • keycode110
  • keysym'n'
  • keysym_num110

また下記のようにイベント処理を設定し、

イベントのプロパティの確認
# appはメインウィンドウのインスタンス
app.bind("<ButtonPress>", func)

アプリ起動後にアプリ上でマウスでクリックすれば、引数 event のプロパティに下記が設定された状態で func 関数が実行され、どの位置がクリックしたかが確認することができます。

  • num1
  • x201
  • y316
  • x_root669
  • y_root546

こんな感じで event 引数にイベントの詳細が格納されていますので、そのイベントの詳細に応じた処理も行うことができます。

例えば下記のスクリプトは event 引数の xy を利用した例になります。

Eventのプロパティの利用例
# -*- coding:utf-8 -*-
import tkinter

# イベント発生時に実行する処理
def func(event):
	global app
	button = tkinter.Button(
		text="button"
	)
	button.place(
		x=event.x,
		y=event.y
	)

# ウィジェットの作成と配置
app = tkinter.Tk()
app.geometry("600x400")

# イベント処理の設定
app.bind("<ButtonPress>", func)

# メインループ
app.mainloop()

アプリを実行してマウスをクリックすると、そのクリックした位置にどんどんボタンが配置されます。

eventのプロパティの利用例

eventxy からクリックされた位置を特定し、その位置に place メソッドでボタンを配置してるだけですが、tkinter.Event のプロパティを利用する例としては分かりやすいのではないかと思います。

ちなみにそのイベントに関係のないプロパティにはまともな値が格納されていない('??' などが格納されている)ので注意しましょう。

例えばマウスに関するイベントでは keysym などには '??' が格納されています。

スポンサーリンク

まとめ

このページでは Tkinter でイベント処理を行う方法について解説しました。

イベント処理は bind メソッドにより設定することができ、下記の3つを指定することであなたが行わせたいイベント処理を実現することができます。

  • どのウィジェット」で
  • どのイベント」が発生した時に
  • どの処理(関数・メソッド)」を実行するか

イベントを使いこなせばユーザーからの操作を受け付け、操作に応じて様々な処理を行うようにすることができます。

特に GUI アプリではイベント処理は重要ですので、この機会に使い方や設定の仕方はしっかり理解しておきましょう!

Tkinter で利用可能なイベントやイベントの発生タイミングについてもっと知りたい方は下記ページも読んでみてください!これらの調べ方について解説しています。

イベントの調べ方の解説ページのアイキャッチTkinter の使い方:利用可能なイベントやイベントが発生するタイミングを調べる

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です