9

在下面的块中,单击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

4 回答 4

16

是的,你可以做你想做的,但这需要一些工作。并不是不支持它,只是实际上很少需要这样的东西,所以它不是默认行为。

TL;DR - 研究“tkinter 绑定标签”

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()

有关绑定标签的更多信息,您可能会对我对如何在 Tkinter Text 小部件中绑定自身事件的问题的回答感兴趣,之后它将被 Text 小部件绑定?. 答案解决了与此处不同的问题,但它显示了使用绑定标签解决现实世界问题的另一个示例。

于 2012-07-12T19:14:32.217 回答
5

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

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_framease.widget.master以使其在必要时可重用。那可能会做你想做的事。

于 2012-07-12T17:57:28.317 回答
0

根据此在线 Tkinter 参考的Levels of Binding部分中所说的内容,这听起来是可能的,因为您可以将处理程序绑定到三个不同的级别。 总结一下:

  1. 实例级别:将事件绑定到特定的小部件。
  2. 类级别:将事件绑定到特定类的所有小部件。
  3. 应用程序级别:独立于小部件——某些事件总是调用特定的处理程序。

详情请参考第一个链接。

希望这可以帮助。

于 2012-07-12T18:04:10.877 回答
0

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

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

tk.bind_class('.', "<Button>", on_frame_click)
于 2019-12-06T21:17:05.477 回答