我有一个带有菜单栏的图形用户界面。我想要能够以编程方式打开这些菜单,就像用户单击它们一样。
我的第一个猜测是使用 "invoke",但这没有任何可见效果。 我知道可以使用 "tk_popup" 打开菜单,但我无法弄清楚坐标。 "yposition" 函数的返回值看起来并不有用。 奇怪的是,我甚至无法获取菜单栏的宽度 - 它始终为 1。
我知道可以使用 "underline" 将 menubutton 绑定到键事件,并且我可能可以通过编程方式创建这样的事件,但我真的不想这样做。
感谢您提前的帮助。
编辑: 我假设您,乔纳森,是指
即使设置了tearoff=True,
关于为什么:我希望用户能够完全自定义键盘快捷方式。因此,我需要一个可以绑定到用户选择的事件的函数。
另一个用例可能是实现帮助功能,该功能不仅告诉用户在哪里找到命令,而且还打开正确的菜单并选择该命令。这将使用户比自己搜索正确的菜单快得多。
我的第一个猜测是使用 "invoke",但这没有任何可见效果。 我知道可以使用 "tk_popup" 打开菜单,但我无法弄清楚坐标。 "yposition" 函数的返回值看起来并不有用。 奇怪的是,我甚至无法获取菜单栏的宽度 - 它始终为 1。
我知道可以使用 "underline" 将 menubutton 绑定到键事件,并且我可能可以通过编程方式创建这样的事件,但我真的不想这样做。
import Tkinter as tk
class MenuBar(tk.Menu):
def __init__(self, root):
tk.Menu.__init__(self, root)
self.root = root
self.menu_file = tk.Menu(m, tearoff=False)
self.menu_file.label = 'File'
self.menu_file.add_command(label='save')
self.menu_file.add_command(label='open')
self.menu_edit = tk.Menu(m, tearoff=False)
self.menu_edit.label = 'Edit'
self.menu_edit.add_command(label='add')
self.menu_edit.add_command(label='remove')
self.menus = (
self.menu_file,
self.menu_edit,
)
for menu in self.menus:
self.add_cascade(label=menu.label, menu=menu, underline=0)
def invoke(self, menu):
if menu in self.menus:
index = self.index(menu.label)
else:
index = menu
print("invoke({!r})".format(index))
tk.Menu.invoke(self, index)
def open_menu(self, menu):
x = self.root.winfo_rootx()
y = self.root.winfo_rooty()
print("yposition: {}".format(self.yposition(self.index(menu.label))))
print("mb.width : {}".format(self.winfo_width()))
print("mb.geometry: {}".format(self.winfo_geometry()))
print("tk_popup({x},{y})".format(x=x, y=y))
menu.tk_popup(x,y)
pass
m = tk.Tk()
mb = MenuBar(m)
m.config(menu=mb)
m.update()
m.bind('f', lambda e: mb.invoke(mb.menu_file))
m.bind('e', lambda e: mb.invoke(mb.menu_edit))
m.bind('<Control-f>', lambda e: mb.open_menu(mb.menu_file))
m.bind('<Control-e>', lambda e: mb.open_menu(mb.menu_edit))
m.mainloop()
感谢您提前的帮助。
编辑: 我假设您,乔纳森,是指
mb.menu_file.invoke(0)
。如果我将tearoff设置为True,那么它可以工作,但这不是我想要的。因为它会在某个地方打开菜单(在我的情况下是屏幕左上角 - 远离窗口),必须通过在窗口右上角的关闭按钮上进行明确的点击来关闭它。即使设置了tearoff=True,
mb.invoke(mb.menu_file)
仍然没有效果(除了打印“invoke(1)”)。
我对postcascade
进行了一些研究,它听起来正是我需要的东西。但正如您已经指出的那样,它不起作用。tcl文档对此有所说明:“如果未发布路径名,则该命令除了取消任何当前发布的子菜单外没有任何效果。”事实上,如果我将m.config(menu=mb)
替换为mb.update(); mb.post(m.winfo_rootx(), m.winfo_rooty())
,它就可以工作了。(在这种情况下,我使用post
而不是tk_popup
,因为它应该保持打开状态。)它仍然不完美,因为我无法使用键盘控制子菜单;但是,无论如何,我想要的是一个菜单栏,而不是一个发布的菜单。
post
打开菜单,但我从this answer中学到,这并不是最好的方法-tk_popup
更好。)该解决方案肯定会提供所需的灵活性。我目前看到的一个缺点是,将鼠标光标移动到下一个菜单时,该菜单不会打开。但应该可以处理。还有其他需要考虑的细节吗?关于为什么:我希望用户能够完全自定义键盘快捷方式。因此,我需要一个可以绑定到用户选择的事件的函数。
另一个用例可能是实现帮助功能,该功能不仅告诉用户在哪里找到命令,而且还打开正确的菜单并选择该命令。这将使用户比自己搜索正确的菜单快得多。
ttk.Menubutton
。 - Novelmb.edit_btn.event_generate('<<Invoke>>')
(在类下更改为self.edit_btn
)放在log
下时,它可以正常工作一次,然后变得有缺陷,即使运行了打印语句也无法重复。 - Nae