Reputation: 11628
I tried to find but all tutorials and documentations I found didn't yield anything. So I made following experiment: There is a simple gui tree that looks like so:
root Tk()
|
+---- Frame a------+------------+
| | |
+---Frame b1--+ Frame b2 Label al
| | |
Frame c Label b1l Label b2l
|
Label cl
Then I set up b1
to invoke <<MyEvent>>
whenever a <Button-1>
was detected on b1l
. Furthermore I bound <<MyEvent>>
to all other root nodes. when running the program and clicking on said label, I could only see that b1
itself and root
reacted to the event, but none of the relatives reacted. So it seems that this event can only be caught by the widget where itw as invoked and in the root widget.
But is there a way to get any of the other widgets to receive this event?
And can anyone recommend a more comprehensive tutorial/documentation that covers things like this?
The code of my experiment is following
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()
Upvotes: 0
Views: 147
Reputation: 385970
In short, events are not propagated. That's simply not how events work in Tkinter. They are only handled by the widget that receives the event. If you want every widget to receive the <<MyEvent>>
event, you'll have to call event_generate
on every widget.
Even though your code makes it appear that root also gets the event, it does not. Here's why it appears that it does:
When a function is called via a binding, it is passed an object representing the event. One of the attributes of this object is widget
, which represents the widget that caught the event. If you print that out along with the string you're already printing, you'll see that it only ever reports the label you clicked on.
For example, change your bind statements to look like this:
...
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})"))
...
For the sake of readability, give each of your widgets a name like so:
...
a = tkinter.Frame(master=root, name="a"); a.pack()
b1 = tkinter.Frame(master=a, name="b1"); b1.pack()
...
The root window has the name ".". The full name of b1
is thus .a.b1
since b1
is a child of a
and a
is a child of the root window.
When I do that and I click on b1l
, this is the output:
MyEvent caught in b1 (.a.b1)
MyEvent caught in root (.a.b1)
Note that in both print statements it shows .a.b1
, even when "root" is handling the event. Even though it looks like root is getting the event, it's actually the b1
widget that gets the event but it is being handled by both the binding on b1
and on root
.
The reason for that is fundamental to how events work in tkinter. Functions aren't actually bound to widgets, but instead are bound to a binding tag. Every widget has a set of binding tags associated with it. By default this set of binding tags is:
When a widget like b1
receives an event, tkinter will traverse the list of binding tags and run any function associated with each tag for that event. So, it goes like this:
b1
(".a.b1"), so that function is calledAt any point, if one of the called function returns the string "break"
, the chain is broken and no more binding tags will be processed.
Upvotes: 1