このページでは Tkinter でのイベントの使い方について解説していきたいと思います。
Contents
イベントとは
まずイベントとは、そのアプリで発生した出来事のことを言います。
イベントの例としては下記のようなものが挙げられます。
- マウスがクリックされた
- マウスが移動した
- アプリ内のボタンが押された
- キーボードの上キーが押された
- ウィンドウサイズが変更された
特に 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(self, sequence=None, func=None, add=None)
bind
というのは「何かと何かを結び付ける」という意味のある英単語です。
特にイベント処理設定に用いる bind
は「イベント」と「処理(関数)」とを結び付けを行います。
この結び付けを行っておけば、その「イベント」が発生した時に結び付けられている「処理」を Tkinter 本体が自動的に実行してくれるようになります。
引数では後述する下記の3つを指定することができます(add
に関してはこのページでは取り扱いません)。
sequence
:「どのイベント」が発生した時にfunc
:「どの処理(関数・メソッド)」を実行するadd
:同じウィジェット・イベントに対して追加でイベント処理を設定するかどうか
そして、bind
メソッドを実行したインスタンスに対してのみ、イベント処理が設定されます。
つまり、bind
メソッドを使用した場合、「どのウィジェット」にイベント処理を設定するかは、bind
メソッドをどのウィジェット(のインスタンス)に実行させるかで指定することができます。
例えば下記のスクリプトを実行すれば、label1
に対してのみイベント処理を設定することができます。
# -*- 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
の上に入れたり離したりすると、アプリの背景が青や白に変わります。
一方で、label2
のラベルや button1
のボタンの上にマウスカーソルを移動させてもアプリの色は変わりません(bg_blue
・bg_white
関数が実行されない)。
これは label1
に対してのみイベント処理を設定して、label2
や button1
にはイベント処理を設定していないためです(label2
や button1
は bind
メソッドを実行していない)。
スポンサーリンク
同じ種類のウィジェットにイベント処理を設定する(bind_class
)
bind
メソッドが1つのウィジェットにイベント処理を設定するのに対し、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
クラスのインスタンスである label1
と label2
に対してイベント処理を設定することができます。
# appはメインウィンドウのインスタンス
# イベント処理の設定
app.bind_class("Label", "<Enter>", bg_blue)
app.bind_class("Label", "<Leave>", bg_white)
今度は label1
と label2
で、マウスカーソルを合わせたり離したりすることでアプリの背景の色が変わることが確認できると思います。
bind
メソッドだとウィジェット1つにしかイベント処理が設定できなかったのに対し、bind_class
だと複数の同じ種類のウィジェットに一度にイベント処理が設定できているところがポイントです。
一方で、button1
のボタンの上にマウスカーソルを移動させてもアプリの色は変わりません(bg_blue
・bg_white
関数が実行されない)。
これは Label
クラスに対してのみイベント処理を設定して、Button
クラスにはイベント処理を設定していないためです(bind_class
の引数に Button
を指定していない)。
全ウィジェットにイベント処理を設定する(bind_all
)
アプリ内の全ウィジェットに対して一括してイベント処理を設定することも可能です。
この場合にはイベント処理設定時に bind_all
メソッドを使用します。
bind_all(self, sequence=None, func=None, add=None)
bind_all
メソッド実行すればアプリ内のウィジェット全てに対してイベント処理を設定することができます。
bind_all
メソッドを実行するのはどのウィジェットのインスタンスでも良く、アプリ内の全ウィジェットに対してイベント処理が設定されます。
例えば1つのウィジェットにイベント処理を設定する(bind
)で紹介したスクリプトの bind
実行部分を下記のように変更すれば、全てのウィジェットに対してイベント処理を設定することができます。
# appはメインウィンドウのインスタンス
# イベント処理の設定
app.bind_all("<Enter>", bg_blue)
app.bind_all("<Leave>", bg_white)
受け付けるイベントの設定
ここまでは主に「どのウィジェット」にイベント処理を設定するかについて解説してきましたが、次は「どのイベント」が発生した時にイベント処理を行うかについて解説していきたいます。
イベントシーケンスの設定
「どのイベント」が発生した時にイベント処理を行うかは、bind
メソッドの引数により指定します(複数のウィジェットにイベント処理を設定する場合は bind_class
や bind_all
を実行しますが、これらでも同様に引数を指定します)。
下記は bind
メソッドの定義の再掲です。
bind(self, sequence=None, func=None, add=None)
「どのイベント」が発生した時にイベント処理を行うかは、引数 sequence
により指定します。この引数 sequence
に指定する情報を「イベントシーケンス」と呼びます。
このイベントシーケンスは下記の形式で表される文字列になります。
"<[modifier-]event_type[-detail]>"
[modifier-]
と [-detail]
は省略可能です。
event_type
、detail
、modifier
にはそれぞれ下記を指定します。
event_type
:イベント処理するイベントの種類- キーボードのキー押し下げ(
KeyPress
)、マウスのボタンの押し下げ(ButtonPress
)等
- キーボードのキー押し下げ(
detail
:イベントの種類の詳細event_type
がキーボードやマウスに関する種類である時、「どの」キー or ボタンに対してイベント処理を行うかを設定(-a
、-b
、-c
、-1
、-2
など)
modifier
:イベント処理する条件- 例えばシフトキーを押された状態でイベントが発生した場合のみイベント処理を行う(
Shift-
)、マウスがダブルクリックされた時のみイベント処理を行う(Double-
)など
- 例えばシフトキーを押された状態でイベントが発生した場合のみイベント処理を行う(
例えば bind
実行時にイベントシーケンスを下記のように指定した場合、キーボードのどのキーを押しても button_func
が実行されます。
これはイベントシーケンスに event_type
のみを指定した例になります。
# appはメインウィンドウのインスタンス
app.bind("<KeyPress>", func)
下記のようにイベントシーケンスを指定した場合はキーボードの “x” キーを押した時のみ button_func
が実行されます。
これは detail
を指定した例になります。
# appはメインウィンドウのインスタンス
app.bind("<KeyPress-x>", func)
さらに下記のようにイベントシーケンスを指定した場合、キーボードの “Controlキー” を押しながら “x” キーが押された時のみ button_func
が実行されるようになります。
これは modifier
を指定した例になります。
# appはメインウィンドウのインスタンス
app.bind("<Control-KeyPress-x>", func)
こんな感じでイベントシーケンスに event_type
を指定してイベント処理を行いたいイベントの種類を設定することができ、さらに detail
と modifier
の指定により「どのイベント」が発生した時にイベント処理を行うかを「細かく」指定することが可能です。
ただし modifier
の設定は私の環境であまり上手く反映されませんでした..
例えば "<Shift-KeyPress-x>"
と設定して、Shift キーを押しながら x キーを押してもイベント処理が行われなかったり…
"<Shift-ButtonPress-2>"
と設定した場合は、Shift キーを押しながらマウスボタンクリックするとイベント処理が上手く動作したりでちょっと謎でした…
環境によって?は上手く動作しない場合もあるようなので注意してください
modifier
は下記のように2つ設定することも可能です。
# 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
に何を設定すれば良いか分からない場合は参考にしてみてください。
スポンサーリンク
実行する処理の設定
次はイベント処理時に「どの処理」を行うかの設定について解説していきたいます。
イベント処理時に実行する処理は関数として定義し、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_num
:keysym
の数字バージョンnum
:マウスボタンに対応した数字send_event
:??serial
:??state
:イベント発生時の状態を表す数字(Control キーが押されていた場合は “4”、など)time
:イベント発生時の時間(絶対時刻ではない?)type
:イベントハンドラを実行する引き金になったイベントの種類widget
:イベントを受け付けたウィジェットwidth
:サイズ変更後の幅x
:ウィジェット上の座標 xx_root
:画面上の座標 xy
:ウィジェット上の座標 yy_root
:画面上の座標 y
これらのプロパティにより、イベント処理時を発生させたイベントがどのようなイベントであるかを調べることが可能です。
例えば下記のようにイベント処理の設定を行い、
# appはメインウィンドウのインスタンス
app.bind("<KeyPress>", func)
アプリ起動後に “n” キーを押せば、この引数 event
のプロパティに下記が設定された状態で func
関数が実行され、どのキーが押されたかを確認することができます。
char
:'n'
keycode
:110
keysym
:'n'
keysym_num
:110
また下記のようにイベント処理を設定し、
# appはメインウィンドウのインスタンス
app.bind("<ButtonPress>", func)
アプリ起動後にアプリ上でマウスでクリックすれば、引数 event
のプロパティに下記が設定された状態で func
関数が実行され、どの位置がクリックしたかが確認することができます。
num
:1
x
:201
y
:316
x_root
:669
y_root
:546
こんな感じで event
引数にイベントの詳細が格納されていますので、そのイベントの詳細に応じた処理も行うことができます。
例えば下記のスクリプトは event
引数の x
、y
を利用した例になります。
# -*- 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
の x
と y
からクリックされた位置を特定し、その位置に place
メソッドでボタンを配置してるだけですが、tkinter.Event
のプロパティを利用する例としては分かりやすいのではないかと思います。
ちなみにそのイベントに関係のないプロパティにはまともな値が格納されていない('??'
などが格納されている)ので注意しましょう。
例えばマウスに関するイベントでは keysym
などには '??'
が格納されています。
スポンサーリンク
まとめ
このページでは Tkinter でイベント処理を行う方法について解説しました。
イベント処理は bind
メソッドにより設定することができ、下記の3つを指定することであなたが行わせたいイベント処理を実現することができます。
- 「どのウィジェット」で
- 「どのイベント」が発生した時に
- 「どの処理(関数・メソッド)」を実行するか
イベントを使いこなせばユーザーからの操作を受け付け、操作に応じて様々な処理を行うようにすることができます。
特に GUI アプリではイベント処理は重要ですので、この機会に使い方や設定の仕方はしっかり理解しておきましょう!
Tkinter で利用可能なイベントやイベントの発生タイミングについてもっと知りたい方は下記ページも読んでみてください!これらの調べ方について解説しています。
Tkinter の使い方:利用可能なイベントやイベントが発生するタイミングを調べる