python - Tkinter:在 Windows 上使用带有菜单的自定义事件
问题描述
使用 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
不是主应用程序的菜单中)。
解决方案
菜单由 OSX 和 Windows 上的操作系统管理,tkinter 几乎无法控制它们的行为。我怀疑将事件发送到其中任何一个平台上的菜单都会起作用。
我会说向菜单发送事件是一种反模式。除非事件与菜单直接相关,否则您应该将事件发送到与菜单关联的顶级窗口、根窗口或具有键盘焦点的窗口,具体取决于事件所代表的内容。
由于您将父小部件作为位置参数传递,因此您可以使用它来获取与菜单关联的顶级窗口。
例如:
class MainMenu(tk.Menu):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
top = parent.winfo_toplevel()
submenu.add_command(
label="foo",
command=lambda: top.event_generate('<<Foo>>')
)
...
推荐阅读
- javascript - 即使在允许脚本访问 Google 表格后,调用 getDataSourceFormula() 时仍然存在授权错误
- flutter - 为什么 sqflite 在热重载时保留开放数据库引用
- python-3.x - 错误的 CPM 和收入值 Google AdManager Report API
- gps - 中国AI思想家A9G GPRS+GPS模块工具
- google-cloud-platform - GCP 工作流程:我们可以设置一个步骤等待其他步骤完成吗?
- split - directx11如何实现分屏效果
- java - 我正在解析 json 数组形式的 api,但它显示错误为“(url)的意外响应代码 403”
- python - 如何将我的supervisord配置从python2转换为python3?
- azure-active-directory - 为托管在 IIS 中的 Asp.net MVC 网站从 Windows 身份验证迁移到 Azure AD 身份验证
- vue.js - 我在使用 npm 或 yarn 构建 vue.js 项目时遇到问题