質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1830閲覧

Tkinterで bind が複数の場合の順番を替えたい

sedo

総合スコア8

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2022/11/29 21:56

前提

python Tkinter でアプリケーションを作ってます。

実現したいこと

<Button>など bind で呼び出される関数が複数ある場合の順番を替えたいです。

該当のソースコード

import

1 2root = tk.Tk() 3root.geometry("200x150") 4 5#ウェジェット↓ 6btn=tk.Button(root,text="ボタン",command=lambda:print("btn_callback")) 7btn.grid() 8 9#バインド 10root.bind("<Button>",lambda e:print("root.bind")) 11btn.bind("<Button-1>",lambda e:print("btn.bind")) 12 13root.mainloop()

試したこと

出力結果
btn.bind
root.bind
btn_callback

例えば root.bind を最初に実行させるなど、
順番を意図的に替える方法を知りたいです。

補足情報

Python 3.9.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

イベント伝搬の順序は子要素→親要素へ流れます。
Release 系イベントは Press 系イベントの後、
Button の command は ButtonRelease-1 イベント内で呼び出される為、
これらの順序を入れ替えることはできません。

イベント自体の順番自体を入れ替えるのは
キーアップ (KeyPress) の後にキーダウン・イベント (KeyRelease)を起こしたい、
みたいな事は仕組み的に無理のある要求なので、通常は
呼び出したい順番になるように適切なイベントハンドラを登録すればよいです。

イベント伝搬を制御する方法は幾つか有ります

  • "break" という文字列を返す事で、親要素への伝搬を差し止めることはできます。
  • 同じウィジェットの同じイベントの bind は add="+" 引数で、追加することができます。
  • 複雑になるような場合は、blinker 等の外部ライブラリを併用します。
    (追記: 情報が少なかったので一旦取りやめ。独自にイベントディスパッチの仕組みを構築します)

root.bind を先に呼び出すだけなら

  • root.bind を ButtonPress イベント btn.bind を ButtonRelease イベント
  • 全て ButtonRelease イベントにして add="+" オプションを指定する
  • btn.bind を btn.bind_all にする ※ root.bind が先に呼ばれますが、他の挙動も変わります。

と、いくつか考えられますが、
root.bind に当たる部分を最初に呼び出したい場合は、
そのイベントハンドラを本当に root に bind すべきかどうかから
検討した方が良いかもしれません。

投稿2022/11/30 04:33

編集2022/11/30 04:45
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TakaiY

2022/11/30 05:41

横からすみません。 tkinter(Tk)のイベントの処理方式についてちょっと調べてもみつけることができなかったのですが、ここを見ればわかるよ、というような情報を教えていただけませんか。
teamikl

2022/11/30 08:06

TakaiY さん 残念ながら包括的な情報源は思い当たる場所がありません。書籍等にあるのかもしれないけど 大抵の場合は tcl/tk の文書 wiki、ソースコード辺りの情報を探すことになります。 まとまった情報がないので個別に返答すると 子要素から親要素へ伝搬するというのは、大抵のGUIライブラリ全般で共通の仕組みです。 ブラウザで用いられる DOM/JavaScript の文書の方が情報が多く比較的よくまとまってます。 イベントの各フェーズに関して、「キャプチャー/ターゲット/バブリング」 「"break" を返すとイベント伝搬をストップ」や「add オプション」の設定方法はtkinter独自の仕様で、 引数に応じて呼び出す tcl のコマンドを動的に変えています。 "break" については確かPythonの公式ドキュメントでは触れられていなくて、 ソースコード内の関数のドキュメントに記載があります。tkinter.Misc.bind の __doc__ ちなみに、JavaScript/DOM である stopPropagation (親へのイベント伝搬を抑制 ... root へイベントが伝搬) や preventDefault (デフォルトの挙動を抑制 ... Release 内で command実行) は、 tkinter では区別できなくて"break" を返すことで両方止めてしまいます。 import tkinter as tk root = tk.Tk() btn = tk.Button(root, text="OK", command=lambda: print("command")) btn.pack() def ignore(event):  print("btn.bind")  return "break" root.bind("<Button>", lambda e: print("root.bind")) btn.bind("<Button>", ignore) root.mainloop() # 「"break" を返す」は、 lambda を使って書くと # btn.bind("<Button>", labmda e: (print("btn.bind"), "break")[-1]) command が Relase 内で呼ばれるというのは、tcl/tk 側の挙動なので、 tcl/tk のドキュメント https://www.tcl.tk/man/tcl/TkCmd/button.html#M6 > This command is typically invoked when mouse button 1 is released over the button window. ソースコード該当箇所 https://github.com/tcltk/bwidget/blob/4de6fb049c01f2f6aca9cacdba90953b7b20cb58/button.tcl#L382 ---- 補足で tkinter の bind の add オプションは boolean でも良いみたいです。(ソースコード内ドキュメントより) tcl/tk 側では同様のオプションで "+" が使われます。https://www.tcl.tk/man/tcl/TkCmd/bind.html 公式ライブラリリファレンスでは、「add はオプションで、 '' か '+' のどちらかです。」 https://docs.python.org/ja/3/library/tkinter.html#bindings-and-events 実際のソースコードでは (add and '+' or '') bool 値により tcl/tk に渡すコマンドを分岐。 どちらでも実行に支障はありません。型チェック(typeshed)でも両方許容するようです。
sedo

2022/11/30 21:40

回答ありがとうございます。 知りたかったこと ・イベント伝搬の順序 子要素→親要素 ・順番の入れ替えは基本的に無理 対策 ・break 又は add を使う ・bind_all に替えてみる 分かりやすい説明で勉強になりました。
teamikl

2022/12/01 04:02

> ・bind_all に替えてみる ここだけは補足が必要で、 処理順が違うことを示すためのデモンストレーションでした。 他での挙動が意図しないものに変わってしまう可能性があり、 順番制御の為の対策としては不適切でした。 対策としては、break add で大抵の場合のイベント制御は賄えるはずです。
TakaiY

2022/12/01 05:24

teamiklさん、説明ありがとうございます。 やっぱり「ソースを見ろ」って感じなんですね(悪い意味でなく)。 自分は昔TkでGUIのツールを作っていたこともあり、そこそこ使える気でいたのですが、まだまだわからないことだらけだなぁと実感しました。 tkinterがpythonということもあり、多くの人が使うようになっていますが、ドキュメントがあまり充実しているとは言えないところがもったいないなと思って見ています。
teamikl

2022/12/01 12:55

tcl/tk は歴史が長いこともあり、割と文書類は充実してるのですが tkinter は python から使えるようにした wrapper ということで、 内部の細かいことは参考リンクを載せて丸投げされてる感じですね。 https://docs.python.org/ja/3/library/tkinter.html の参考リンク集 tkinter として探したときに情報が少ないのは解ります。 他に、よく参考にするサイトを挙げるなら Tcl/Tk の wiki かな。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問