tkinter で 低層のウィジェットでイベントを検出する
tkinterでイベントを検出する際、対象のウィジェットの手前に別のウィジェットがあるとイベントを検出できません。
それを回避し、手前にウィジェットが存在してもイベントが検出できるサンプルプログラムを書いてみました。
さっそくサンプルプログラムを貼りつけておきます。
以下の部分が普通にcanvasにマウスクリックイベントをバインドしている部分です。
self.test_canvas.bind('<ButtonPress-1>', self._on_click_canvas)
canvas上(水色の部分)でクリックすると_on_click_canvas
が実行され、
_on_click_canvas : click on canvas
と表示されます。
しかし、その上に配置されたラベル(labex_1x)上でクリックしたときは表示されません。
labelもcanvasの一部なので、ここでもクリック処理が動いてほしいことはよくあると思います。
そこで、以下の部分でrootにマウスクリックイベントをバインドします。
self.winfo_toplevel().bind('<ButtonPress-1>', self._on_click, "+")
これはrootにバインドされたイベントですから、当然canvas以外の部分でも処理が動きます。
そこで、イベントハンドラでマウス座標を取得し、canvasの範囲内かを確認し、
そうであればクリック処理(ここでは_on_click : click on canvas
を表示)を実行します。
パラメータの最後の"+"
は、既にバインドされているハンドラがあった時、上書きせずに追加することを指定しています。
(つまり、以前にバインドしたハンドラと今回のハンドラ両方が実行されます)
こちらの処理はcanvas上(水色の部分)でクリックしたときも その上に配置されたラベル(labex_1x)上でクリックしたときもクリック処理が実行されます。
なお、イベントハンドラに渡されるパラメータevent
のevent.x
、event.y
はウィジェット内の相対座標なので
比較にはevent.x_root
、event.y_root
を使用して画面上の座標を使用します。
当然比較するcanvasの座標もwidget.winfo_rootx()
、widget.winfo_rooty()
で画面上の座標を使用しなければなりません。
おまけとして、ダブルクリックした位置に存在するウィジェットの一覧を表示する処理を入れておきました(_on_doubleclick
) 。
ダブルクリックに意味はなく、クリックは既に使っていたのでダブルクリックにしただけです。
おーちゃくせずに別のプログラム書けよ > 自分
event.widget
から 順に master
をたどって行き、対象のウィジェットが見つかるかで判断する方法もあるなぁ…
rootの master
は None
なのでそこでサーチ終了。
どっちが簡単かな…
bind
するときに、.winfo_children() を再帰的にサーチしてすべての子ウィジェットにbindしていく、という手もあるなぁ。
いっぱいウィジェット配置してるとえらいことになるかもしれんけど…