如何捕获tkinter子窗口小部件上的事件?

时间:2012-07-12 16:44:53

标签: python events tkinter

在下面的街区中,点击a_frame会触发事件处理程序on_frame_click,但点击a_label的子a_frame则不会。有没有办法强制a_frame陷阱和处理源自其子项的事件(最好不必直接向子项添加处理程序)?我使用的是Python 3.2.3。

import tkinter

def on_frame_click(e):
    print("frame clicked")

tk = tkinter.Tk()
a_frame = tkinter.Frame(tk, bg="red", padx=20, pady=20)
a_label = tkinter.Label(a_frame, text="A Label")
a_frame.pack()
a_label.pack()
tk.protocol("WM_DELETE_WINDOW", tk.destroy)
a_frame.bind("<Button>", on_frame_click)
tk.mainloop()

4 个答案:

答案 0 :(得分:13)

是的,你可以做你想做的事,但这需要一些工作。并不是说它不受支持,只是因为它实际上非常罕见,因此它不是默认行为。

TL; DR - 研究“tkinter bind tags”

Tkinter事件模型包含“绑定标记”的概念。这是与每个小部件关联的标签列表。在窗口小部件上收到事件时,将检查每个绑定标记以查看它是否具有该事件的绑定。如果是,则调用处理程序。如果没有,它继续。如果处理程序返回“break”,则链断开,不再考虑标记。

默认情况下,窗口小部件的绑定标记是窗口小部件本身,窗口小部件类,窗口小部件所在的顶层窗口的标记,最后是特殊标记“all”。但是,您可以在其中放置所需的任何标签,并且可以更改顺序。

所有这些的实际结果?您可以将自己的唯一标记添加到每个窗口小部件,然后将单个绑定添加到将由所有窗口小部件处理的标记。这是一个例子,使用你的代码作为起点(我添加了一个按钮小部件,以显示这不是特别适用于框架和标签的东西):

import Tkinter as tkinter

def on_frame_click(e):
    print("frame clicked")

def retag(tag, *args):
    '''Add the given tag as the first bindtag for every widget passed in'''
    for widget in args:
        widget.bindtags((tag,) + widget.bindtags())

tk = tkinter.Tk()
a_frame = tkinter.Frame(tk, bg="red", padx=20, pady=20)
a_label = tkinter.Label(a_frame, text="A Label")
a_button = tkinter.Button(a_frame, text="click me!")
a_frame.pack()
a_label.pack()
a_button.pack()
tk.protocol("WM_DELETE_WINDOW", tk.destroy)
retag("special", a_frame, a_label, a_button)
tk.bind_class("special", "<Button>", on_frame_click)
tk.mainloop()

有关bindtags的更多信息,您可能会对问题my answer感兴趣How to bind self events in Tkinter Text widget after it will binded by Text widget?。答案解决了与此处不同的问题,但它显示了使用绑定标记解决实际问题的另一个例子。

答案 1 :(得分:4)

我似乎无法找到自动绑定到子窗口小部件的直接方法(尽管有一些绑定到整个类窗口小部件和应用程序中所有窗口小部件的方法),但这样的事情很容易。

def bind_tree(widget, event, callback, add=''):
    "Binds an event to a widget and all its descendants."

    widget.bind(event, callback, add)

    for child in widget.children.values():
        bind_tree(child, event, callback, replace_callback)

想到这一点,但你也可以在a_frame的孩子身上放置一个大小为a_frame的透明小部件,并将<Button>事件绑定到那个,并且那么您可以在回调中将a_frame称为e.widget.master,以便在必要时使其可重复使用。那可能就是你想要的。

答案 2 :(得分:0)

根据它在Levels of Bindingonline Tkinter reference部分中所说的内容,听起来似乎是可能的,因为您可以将处理程序绑定到三个不同的级别。
总结:

  1. 实例级别:将事件绑定到特定小部件。
  2. 班级级别:将事件绑定到特定班级的所有小部件。
  3. 应用程序级别:独立于小组件 - 某些事件始终会调用特定的处理程序。
  4. 有关详细信息,请参阅第一个链接。

    希望这有帮助。

答案 3 :(得分:0)

根据您要执行的操作,可以绑定所有内容

print(a_label.bindtags())  # ('.!frame.!label', 'Label', '.', 'all')

tk.bind_class('.', "<Button>", on_frame_click)