我有一个带有菜单栏的图形用户界面。我希望能够以编程方式打开这些菜单,就像用户点击它们一样。
我的第一个猜测是,invoke但这没有明显的效果。我知道我可以使用打开菜单,tk_popup但我无法弄清楚坐标。该yposition函数的返回值看起来没有帮助。奇怪的是,我什至无法获得菜单栏的宽度 - 它始终为 1。
我知道我可以将一个菜单按钮绑定到一个关键事件,underline并且我可能会以编程方式创建这样一个事件,但我真的不想这样做。
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()
Run Code Online (Sandbox Code Playgroud)
提前致谢。
编辑:
我假设你,乔纳森,指的是mb.menu_file.invoke(0). 如果我将 tearoff 设置为 True,这会起作用,是的,但这不是我想要的。因为这会在某个单独的窗口中打开菜单(在我的情况下在屏幕的左上角 - 远离窗口)并且必须通过明确单击窗口右上角的关闭按钮来关闭它。
即使使用 tearoff=Truemb.invoke(mb.menu_file)仍然没有效果(除了打印“invoke(1)”)。
我已经做了一些研究postcascade,这听起来和我正在寻找的完全一样。除此之外 - 正如您已经指出的那样 - 它不起作用。在TCL文档上说这个问题:“如果路径名是不贴,该命令具有除了unpost任何当前发布的子菜单没有任何影响”。事实上,如果我取代m.config(menu=mb)由mb.update(); mb.post(m.winfo_rootx(), m.winfo_rooty())它的工作原理。(我在这里使用post而不是tk_popup因为在这种情况下它应该保持打开状态。)它仍然不完美,因为我无法用键盘控制子菜单;但是,无论如何,我想要一个菜单栏,而不是张贴的菜单。
你知道“伪造”菜单有什么缺点吗?我没有考虑这样做,因为effbot声明“由于此小部件尽可能使用本机代码,因此您不应该尝试使用按钮和其他 Tkinter 小部件来伪造菜单。” (另一方面,本文还建议使用 来打开菜单post,我从这个答案中了解到这不是最好的方法 -tk_popup更好。)该解决方案肯定会提供所需的灵活性。我目前看到的一个缺点是,当将鼠标光标移动到下一个菜单时,该菜单不会打开。但是应该可以处理。我需要考虑更多细节吗?
关于原因:我希望用户能够完全自定义键盘快捷键。因此,我需要一个可以绑定到用户选择的事件的函数。
另一个用例可能是实现帮助功能,它不仅告诉用户在哪里可以找到命令,而且打开正确的菜单并选择该命令。这将使用户找到它比自己搜索正确的菜单要快得多。
invoke命令相当于tearoff命令。如果你允许撕下,你就会看到这项工作。
您正在寻找的命令是“postcascade”。没有 tkinter 绑定到此命令,如果您手动调用它 ( root.tk.eval(str(mb)+' postcascade 1') 什么也不会发生,这可能就是没有绑定的原因。
我尝试了很多其他的事情,但我无法让它发挥作用。
然而,tk 也有一个 Menubutton 小部件,它可以响应事件<<Invoke>>。所以(如果你真的想要这个功能)你可以制作自己的菜单栏:
import Tkinter as tk
import ttk
def log(command):
print 'running {} command'.format(command)
class MenuBar(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master, bd=1, relief=tk.RAISED)
file_btn = tk.Menubutton(self, text='File')
menu_file = tk.Menu(file_btn, tearoff=False)
menu_file.add_command(label='save', command=lambda: log('save'))
menu_file.add_command(label='open', command=lambda: log('open'))
file_btn.config(menu=menu_file)
file_btn.pack(side=tk.LEFT)
master.bind('f', lambda e: file_btn.event_generate('<<Invoke>>'))
edit_btn = tk.Menubutton(self, text='Edit')
menu_edit = tk.Menu(edit_btn, tearoff=False)
menu_edit.add_command(label='add', command=lambda: log('add'))
menu_edit.add_command(label='remove', command=lambda: log('remove'))
edit_btn.config(menu=menu_edit)
edit_btn.pack(side=tk.LEFT)
master.bind('e', lambda e: edit_btn.event_generate('<<Invoke>>'))
m = tk.Tk()
m.geometry('300x300')
mb = MenuBar(m)
mb.pack(side=tk.TOP, fill=tk.X)
m.mainloop()
Run Code Online (Sandbox Code Playgroud)
使用热键打开菜单后,可以使用箭头键进行导航,可以使用 Enter 键运行所选选项,或使用退出键关闭。
| 归档时间: |
|
| 查看次数: |
1999 次 |
| 最近记录: |