I want to escalate an event, in this case left click. I want that when inside the inner, green area is "clicked", the event of the inner area and the outer area are executed.
When all the code is in the same class, I have solved it (following an example), but not understood :-( . It is made explicit in the statements, commented, bindtags.
I don't know how to extrapolate that logic / those sentences to the case of being implemented in different classes, as in the example that follows.
import tkinter as tk
from tkinter import ttk
class Example(object):
def __init__(self, parent):
super().__init__()
style = ttk.Style()
style.configure('Fuera.TFrame', background='Yellow')
fuera = ttk.Frame(parent, style='Fuera.TFrame', height= 100, width=100)
Dentro(fuera)
fuera.bind('<Button-1>', self.fueraClick)
fuera.pack(expand=1)
# bindtags = list(dentro.bindtags())
# bindtags.insert(1, fuera)
# dentro.bindtags(tuple(bindtags))
def fueraClick(self, event):
print("Click en zona exterior")
class Dentro(object):
def __init__(self, master):
super().__init__()
style = ttk.Style()
style.configure('Dentro.TFrame', background='Green')
dentro = ttk.Frame(master, style='Dentro.TFrame', height= 50, width=50)
dentro.bind('<Button-1>', self.dentroClick)
dentro.pack(padx=30, pady=30)
def dentroClick(self, event):
print("Click en zona interior")
if __name__ == '__main__':
root = tk.Tk()
Example(root)
root.mainloop()
I have understood that binds are saved globally and there is some way to manipulate them, but as I have indicated I have not understood the logic. An explanation, for dummies, of the implementation philosophy and an example to clarify it, would help me a lot.
Tkinter's binding system is based on so-called "bindtags". Despite the years of Tk/TCL and the fact that it is largely the conceptual inspiration for most of the modern frameworks that have copied or perfected the concepts that Tk introduced, its binding system is still possibly one of, if not the most flexible and powerful of all.
When a widget is bound to an event, what is actually associated is not the widget itself, it is always a tag.
Each widget has four associated labels by default, although it can have more, less, or none. When an event is detected in a widget, each tag is checked to see if there is a link for that event. If it is found, the callback associated with the link is executed and the next widget tag is passed, until the entire list of tags is traversed. or if one of the callbacks returns "break", which breaks the event propagation through the list of labels.
The default list of tags is:
widget.bind
by default binds to this tagwidget.bind_class
binds by default to this tag"all"
->widget.bind_all
bind by default to this tag (bind global)and are checked in that order. It seems simple, but the potential is enormous. For example, in widgets
Entry
, when a key is pressed, the event arrives first at the widget tag itself as expected, but the character is not inserted at this point, that is done by the binding associated with the class tag. If we get in the way at this point we can handle the event and prevent or allow something to be inserted into the widget regardless of whether the key was pressed, this is the mechanism behind the validation of the entries, which are processed at the tag level of the widget. In this way, if the character is not validated, the event is broken and it does not reach the class, preventing the character from even being entered in the Entry.We can also add our own tags and place them in the position that interests us so that they are processed before or after as we see fit.
If we look at the list of default bindtags, there aren't any that point to the parent widget, because in Tkinter events don't usually propagate to the parent widget, except for a handful of cases that usually involve base widgets like the same
tkinter.Tk
ortkinter.Toplevel
. Just because it doesn't propagate to the parent by default doesn't mean it can't be done, the flexibility of binding tags comes to the rescue again, we can bind events to other widgets that don't even have to be direct parents of a given widget, just insert the tag associated with said widgets in the list of bindtags of the widget that first receives the event and it will "spread" wherever we want and as many widgets as we want.In your case, the main error is that you try to modify the bindtags of the wrong widget. When the inner frame is clicked, it is this widget that receives the event and therefore its bindtags are processed. It is in the list of bindtags of the child widget that you have to insert the bindtag of the parent:
Note that if you wanted the parent's callback to be called before the child's, just alter the order of the bindtags:
simple, but with enormous flexibility, because in addition, this second widget can, through its bindtags, propagate the event to others or cut it, returning
"break"
at any time.In response to comments
An example hooking the event also in root
The use of
bind_all
The application of
bind_all
is when we want to bind global events, that is, we want something to happen regardless of the application widget that receives the event. A typical case may be the typical right-click popup menu present in many apps.