【问题标题】:How to pass an event to parent tkinter widget?如何将事件传递给父 tkinter 小部件?
【发布时间】:2021-01-07 23:56:27
【问题描述】:

是否可以在 Tkinter 中将事件直接传递给父窗口小部件?

我有一个画布,上面覆盖着其他画布的网格(是复数吗?),这是我使用parent_canvas.create_window() 方法添加的。我希望某些事件(例如鼠标释放事件)由父画布处理。

如果我只将事件绑定到父方法,event.xevent.y 坐标相对于捕获事件的子画布出来。

【问题讨论】:

  • 创建一个画布子类,您可以在其中创建父画布属性。然后使用构造函数中定义的属性稍后绑定到父画布。
  • 为什么要在画布中嵌入画布?好像有点过分了。
  • @MalikBrahimi:问题是 event.x 和 event.y 坐标相对于捕获事件的子画布出来。
  • @steffen 将事件对象传递给父属性的bind 函数。
  • @BryanOakley:那是因为我正在绘制单独的情节,结果证明处理超出范围的情节部分相当困难。

标签: python tkinter


【解决方案1】:

Tkinter 不会将事件传递给父小部件。但是,您可以通过使用绑定标签(或“bindtags”)来模拟效果。

我能给出的最短解释是:当您向小部件添加绑定时,您并不是在向小部件添加绑定,而是在绑定到“绑定标签”。此标签与小部件具有相同的名称,但它实际上不是小部件。

小部件有一个绑定标签列表,因此当小部件上发生事件时,每个标签的绑定都会按顺序处理。通常顺序是:

  1. 实际小部件上的绑定
  2. 小部件类上的绑定
  3. 包含小部件的*小部件上的绑定
  4. “全部”上的绑定

请注意,该列表中没有“父小部件上的绑定”。

您可以将自己的绑定标签插入到该顺序中。因此,例如,您可以将主画布添加到每个子画布的绑定标签中。当您绑定到任何一个时,都会调用该函数。因此,似乎事件已传递给父级。

这是一些用 python 2.7 编写的示例代码。如果单击灰色方块,您会看到打印出两件事,显示子画布上的绑定和主画布上的绑定都触发了。如果单击粉红色方块,您会看到子画布绑定触发,但它阻止父绑定触发。

这样,所有的按钮点击实际上都“传递”给了父级。子画布可以控制父级是否应该处理事件,如果要“中断”事件处理链,则返回“break”。

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.main = tk.Canvas(self, width=400, height=400, 
                              borderwidth=0, highlightthickness=0,
                              background="bisque")
        self.main.pack(side="top", fill="both", expand=True)

        # add a callback for button events on the main canvas
        self.main.bind("<1>", self.on_main_click)

        for x in range(10):
            for y in range(10):
                canvas = tk.Canvas(self.main, width=48, height=48, 
                                   borderwidth=1, highlightthickness=0,
                                   relief="raised")
                if ((x+y)%2 == 0):
                    canvas.configure(bg="pink")

                self.main.create_window(x*50, y*50, anchor="nw", window=canvas)

                # adjust the bindtags of the sub-canvas to include
                # the parent canvas
                bindtags = list(canvas.bindtags())
                bindtags.insert(1, self.main)
                canvas.bindtags(tuple(bindtags))

                # add a callback for button events on the inner canvas
                canvas.bind("<1>", self.on_sub_click)


    def on_sub_click(self, event):
        print "sub-canvas binding"
        if event.widget.cget("background") == "pink":
            return "break"

    def on_main_click(self, event):
        print "main widget binding"

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack (fill="both", expand=True)
    root.mainloop()

【讨论】: