【问题标题】:How to capture events on tkinter child widgets?如何捕获 tkinter 子小部件上的事件?
【发布时间】:2012-07-12 11:40:26
【问题描述】:

在下面的代码块中,点击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()

【问题讨论】:

    标签: python events tkinter


    【解决方案1】:

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

    print(a_label.bindtags())  # ('.!frame.!label', 'Label', '.', 'all')
    
    tk.bind_class('.', "<Button>", on_frame_click)
    

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • bindtags 像冠军一样工作!我唯一不同的是a_label.bindtags((a_frame,) + a_label.bindtags()),它使b_frame.bind("&lt;Button&gt;", on_frame_click)同时绑定了标签和框架,并在回调中给了我框架的ID为e.widget.bindtags()[0]作为额外的奖励。这使我能够区分不同的层次结构。谢谢
      【解决方案3】:

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

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

      详情请参考第一个链接。

      希望这会有所帮助。

      【讨论】:

      • 谢谢。实例级别绑定是我在原始问题中展示的内容。类级绑定也有同样的问题,点击标签在触发框架的事件处理程序方面不算点击框架。与 JAB 的解决方案类似,应用程序级绑定是一种更快(但粒度较小)的方法。在这种情况下,回调仍会将标签报告为事件源,因此回调必须足够复杂,才能爬回包含层次结构以找到有问题的父级,如果我找不到更好的选择。
      • 使用应用程序级绑定意味着回调必须区分三种情况 1) 源小部件是框架吗? 2)源是框架的孩子吗? 3)是其他东西的来源。如果我有几种不同类型的包含层次结构需要具有不同的行为,这可能会变得非常复杂。
      • “绑定级别”页面只讲述了部分内容。不只是三个级别,默认情况下有四个级别,并且可以添加无限数量的级别(尽管“级别”有点用词不当——它更像是一个序列)。这是 Tkinter 的一个未充分记录的特性,但它使 Tkinter 领先于几乎所有其他工具包的事件机制。有关更多信息,请在“绑定标签”上谷歌(或阅读我对这个问题的回答)
      【解决方案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 的子元素放在所有内容之上,并将&lt;Button&gt; 事件绑定到该小部件,然后您可以参考@ 987654325@ 作为e.widget.master 在回调中,以便在必要时使其可重用。这可能会做你想做的事。

      【讨论】:

      • 这肯定会触发回调,但事件的 source 仍然是子元素,而不是父元素。在我的示例回调中,这不是问题(因为未引用源)。但是,如果我想在父级上启动拖放操作,这会使回调复杂化。
      • @Monkeyer 我明白了。在那种情况下,我只是想到了你可以尝试的其他方法。
      • 请注意,必须已经创建了子级才能使其工作。在尝试将其放入构造函数的情况下,这是一个问题。为了解决这个问题,我重写了更新方法来进行正常更新并进行绑定。在创建完所有的孩子之后,我调用 update 来填充绑定。可能有更好的方法,但这可行。
      猜你喜欢
      • 2023-01-13
      • 2020-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-30
      • 1970-01-01
      相关资源
      最近更新 更多