使用python 3.9.4
为了实现松散耦合,我尝试在tkinter中实现一个生成自定义事件的主菜单,而不是直接调用回调函数。示例脚本显示了基本方法:
import tkinter as tk
root = tk.Tk()
root.geometry('300x200')
label = tk.Label(text='Test')
label.grid()
menu = tk.Menu(root)
root.configure(menu=menu)
submenu = tk.Menu(menu)
menu.add_cascade(menu=submenu, label='Change Text')
submenu.add_command(
label='Foo',
command=lambda: menu.event_generate('<<Foo>>')
)
submenu.add_command(
label='Bar',
command=lambda: menu.event_generate('<<Bar>>')
)
def setfoo(*_):
label.configure(text='Foo')
def setbar(*_):
label.configure(text='Bar')
menu.bind('<<Foo>>', setfoo)
menu.bind('<<Bar>>', setbar)
root.mainloop()
这种方法在linux中有效,但在windows上绑定不起作用。它们似乎已绑定到事件,但选中菜单项时不会发生任何操作。
我假设这与windows和linux上的菜单实现之间的差异有关,但是有没有正确的方法或者解决方法呢?
编辑:为了帮助大家更好地理解我为什么要做某些事情,这里有一个面向对象的版本,它与我使用tkinter的方式更为一致:
import tkinter as tk
class MainMenu(tk.Menu):
def __init__(self, *args,**kwargs):
super().__init__(*args,**kwargs)
submenu = tk.Menu(self)
submenu.add_command(
label="foo",
command=lambda: self.event_generate('<<Foo>>')
)
submenu.add_command(
label="bar",
command=lambda: self.event_generate('<<Bar>>')
)
self.add_cascade(menu=submenu, label='Test')
class Application(tk.Tk):
def __init__(self, *args,**kwargs):
super().__init__(*args,**kwargs)
menu = MainMenu(self)
self.configure(menu=menu)
menu.bind('<<Foo>>', print)
menu.bind('<<Bar>>', print)
if __name__ == '__main__':
app = Application()
app.mainloop()
请注意 MainMenu
类将在单独的文件中定义,因此 app
全局不可用。我可以用 MainMenu.master
但这打破了松散耦合,因为它假定根窗口是此类的父级。这种假设可能会被打破(比如主窗口被放进汉堡包菜单中,或者被添加到另一个菜单中) TopLevel
这不是主要的应用程序)。
2条答案
按热度按时间5us2dqdw1#
菜单由osx和windows上的操作系统管理,tkinter对它们的行为几乎没有控制权。我怀疑在这两个平台上向菜单发送事件是否可行。
我想说,将事件发送到菜单是一种反模式。除非事件与菜单直接相关,否则应将事件发送到与菜单关联的顶级窗口、根窗口或具有键盘焦点的窗口,具体取决于事件所表示的内容。
由于您将父窗口小部件作为位置参数传入,因此可以使用它来获取与菜单关联的顶级窗口。
例如:
xam8gpfp2#
我想我知道了,但不是100%确定。
看看这个代码:
代码运行100毫秒后,
!
出现在屏幕上,但我对text.pack()
,屏幕上不显示任何内容。如果我改变了root.after(100, text.event_generate, "<<Foo>>")
至text.event_generate("<<Foo>>")
,屏幕上不显示任何内容。这证明了@bryanoakley的理论,即它与widget的可见性有关。这意味着问题很可能出在tcl
/功能tcl
电话。解决方案是使用一个虚拟小部件,或者像@matiiss建议的那样使用它
root.event_generate
以及root.bind
.