将事件传播到比 Widget 更远的地方?
Propagate Events farther than within Widget?
- 是否可以将事件发送到比创建它的小部件更多的其他小部件?
我试图找到,但我找到的所有教程和文档都没有产生任何结果。所以我做了以下实验:有一个简单的 gui 树,看起来像这样:
root Tk()
|
+---- Frame a------+------------+
| | |
+---Frame b1--+ Frame b2 Label al
| | |
Frame c Label b1l Label b2l
|
Label cl
然后我将 b1
设置为在 b1l
上检测到 <Button-1>
时调用 <<MyEvent>>
。此外,我将 <<MyEvent>>
绑定到所有其他根节点。当 运行 程序并单击该标签时,我只能看到 b1
本身和 root
对事件做出了反应,但是 none 的亲戚做出了反应。所以看起来这个事件只能被调用它的小部件和根小部件捕获。
但是有没有办法让任何其他小部件接收此事件?
谁能推荐更全面的 tutorial/documentation 涵盖此类内容?
我的实验代码如下
import tkinter
root = tkinter.Tk()
a = tkinter.Frame(master=root); a.pack()
al = tkinter.Label(master=a, text="label a "); al .pack()
b1 = tkinter.Frame(master=a); b1.pack()
b1l = tkinter.Label(master=b1, text="label b1"); b1l.pack()
c = tkinter.Frame(master=b1); c.pack()
cl = tkinter.Label(master=c, text="label c1 "); cl .pack()
b2 = tkinter.Frame(master=a); b2.pack()
b2l = tkinter.Label(master=b2, text="label b2"); b2l.pack()
root.bind('<<MyEvent>>', lambda *_: print("MyEvent caught in root"))
a. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in a "))
al. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in al "))
b1. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b1 "))
b1l. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b1l "))
b2. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b2 "))
b2l. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b2l "))
c. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in c "))
cl. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in cl "))
def cb(*x):
b1.event_generate('<<MyEvent>>')
b1l.bind('<Button-1>', cb)
root.mainloop()
简而言之,事件不会传播。这根本不是 Tkinter 中事件的工作方式。它们仅由接收事件的小部件处理。如果您希望每个小部件都接收 <<MyEvent>>
事件,则必须在每个小部件上调用 event_generate
。
即使您的代码看起来 root 也获得了事件,但实际上并没有。这就是它看起来如此的原因:
当通过绑定调用函数时,它会传递一个表示事件的对象。该对象的属性之一是 widget
,它表示捕获事件的小部件。如果您将它与您已经在打印的字符串一起打印出来,您会发现它只会报告您点击的标签。
例如,将您的绑定语句更改为如下所示:
...
root.bind('<<MyEvent>>', lambda event: print(f"MyEvent caught in root ({event.widget})"))
b1. bind('<<MyEvent>>', lambda event: print(f"MyEvent caught in b1 ({event.widget})"))
...
为了便于阅读,请为每个小部件命名,如下所示:
...
a = tkinter.Frame(master=root, name="a"); a.pack()
b1 = tkinter.Frame(master=a, name="b1"); b1.pack()
...
根 window 的名称为“.”。因此,b1
的全名是 .a.b1
,因为 b1
是 a
的子节点,而 a
是根 window 的子节点。
当我这样做并单击 b1l
时,这是输出:
MyEvent caught in b1 (.a.b1)
MyEvent caught in root (.a.b1)
请注意,在两个打印语句中它都显示 .a.b1
,即使“root”正在处理该事件。尽管看起来 root 正在获取事件,但实际上是 b1
小部件获取了事件,但它是由 b1
和 root
.[=29 上的绑定处理的=]
其原因是事件在 tkinter 中的工作方式的基础。函数实际上并未绑定到小部件,而是绑定到 绑定标签。每个小部件都有一组与之关联的绑定标签。默认情况下,这组绑定标签是:
- 小部件的全名(例如:“.a.b1”)
- 小部件的 class(“框架”)
- 根的名称window (".")
- 特殊标签“全部”
当像 b1
这样的小部件接收到事件时,tkinter 将遍历绑定标签列表和 运行 与该事件的每个标签关联的任何函数。所以,它是这样的:
b1
(".a.b1") 上有一个绑定,因此调用了该函数
- “框架”没有绑定,所以没有调用任何东西
- 在根 window (".") 上有一个绑定,因此调用了该函数
- 特殊标签“all”没有绑定。
在任何时候,如果被调用函数之一 returns 字符串 "break"
,则链被破坏,将不再处理绑定标签。
- 是否可以将事件发送到比创建它的小部件更多的其他小部件?
我试图找到,但我找到的所有教程和文档都没有产生任何结果。所以我做了以下实验:有一个简单的 gui 树,看起来像这样:
root Tk()
|
+---- Frame a------+------------+
| | |
+---Frame b1--+ Frame b2 Label al
| | |
Frame c Label b1l Label b2l
|
Label cl
然后我将 b1
设置为在 b1l
上检测到 <Button-1>
时调用 <<MyEvent>>
。此外,我将 <<MyEvent>>
绑定到所有其他根节点。当 运行 程序并单击该标签时,我只能看到 b1
本身和 root
对事件做出了反应,但是 none 的亲戚做出了反应。所以看起来这个事件只能被调用它的小部件和根小部件捕获。
但是有没有办法让任何其他小部件接收此事件?
谁能推荐更全面的 tutorial/documentation 涵盖此类内容?
我的实验代码如下
import tkinter
root = tkinter.Tk()
a = tkinter.Frame(master=root); a.pack()
al = tkinter.Label(master=a, text="label a "); al .pack()
b1 = tkinter.Frame(master=a); b1.pack()
b1l = tkinter.Label(master=b1, text="label b1"); b1l.pack()
c = tkinter.Frame(master=b1); c.pack()
cl = tkinter.Label(master=c, text="label c1 "); cl .pack()
b2 = tkinter.Frame(master=a); b2.pack()
b2l = tkinter.Label(master=b2, text="label b2"); b2l.pack()
root.bind('<<MyEvent>>', lambda *_: print("MyEvent caught in root"))
a. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in a "))
al. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in al "))
b1. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b1 "))
b1l. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b1l "))
b2. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b2 "))
b2l. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in b2l "))
c. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in c "))
cl. bind('<<MyEvent>>', lambda *_: print("MyEvent caught in cl "))
def cb(*x):
b1.event_generate('<<MyEvent>>')
b1l.bind('<Button-1>', cb)
root.mainloop()
简而言之,事件不会传播。这根本不是 Tkinter 中事件的工作方式。它们仅由接收事件的小部件处理。如果您希望每个小部件都接收 <<MyEvent>>
事件,则必须在每个小部件上调用 event_generate
。
即使您的代码看起来 root 也获得了事件,但实际上并没有。这就是它看起来如此的原因:
当通过绑定调用函数时,它会传递一个表示事件的对象。该对象的属性之一是 widget
,它表示捕获事件的小部件。如果您将它与您已经在打印的字符串一起打印出来,您会发现它只会报告您点击的标签。
例如,将您的绑定语句更改为如下所示:
...
root.bind('<<MyEvent>>', lambda event: print(f"MyEvent caught in root ({event.widget})"))
b1. bind('<<MyEvent>>', lambda event: print(f"MyEvent caught in b1 ({event.widget})"))
...
为了便于阅读,请为每个小部件命名,如下所示:
...
a = tkinter.Frame(master=root, name="a"); a.pack()
b1 = tkinter.Frame(master=a, name="b1"); b1.pack()
...
根 window 的名称为“.”。因此,b1
的全名是 .a.b1
,因为 b1
是 a
的子节点,而 a
是根 window 的子节点。
当我这样做并单击 b1l
时,这是输出:
MyEvent caught in b1 (.a.b1)
MyEvent caught in root (.a.b1)
请注意,在两个打印语句中它都显示 .a.b1
,即使“root”正在处理该事件。尽管看起来 root 正在获取事件,但实际上是 b1
小部件获取了事件,但它是由 b1
和 root
.[=29 上的绑定处理的=]
其原因是事件在 tkinter 中的工作方式的基础。函数实际上并未绑定到小部件,而是绑定到 绑定标签。每个小部件都有一组与之关联的绑定标签。默认情况下,这组绑定标签是:
- 小部件的全名(例如:“.a.b1”)
- 小部件的 class(“框架”)
- 根的名称window (".")
- 特殊标签“全部”
当像 b1
这样的小部件接收到事件时,tkinter 将遍历绑定标签列表和 运行 与该事件的每个标签关联的任何函数。所以,它是这样的:
b1
(".a.b1") 上有一个绑定,因此调用了该函数- “框架”没有绑定,所以没有调用任何东西
- 在根 window (".") 上有一个绑定,因此调用了该函数
- 特殊标签“all”没有绑定。
在任何时候,如果被调用函数之一 returns 字符串 "break"
,则链被破坏,将不再处理绑定标签。