如何在按下按钮时查看菜单?
How to view a menu when a button is pressed?
跟进 ,我正在尝试查看(与单击鼠标左键时相同)菜单 sub1
,当按钮 Test
是按下,但我不能。在以下代码中,按钮似乎冻结了 GUI:
import tkinter as tk
root = tk.Tk()
menubar = tk.Menu(root)
sub1 = tk.Menu(menubar, tearoff=0)
sub1.add_command(label="Item 1", command=lambda : print("item 1"))
sub1.add_command(label="Item 2", command=lambda : print("item 2"))
menubar.add_cascade(menu=sub1, label="Sub1", underline=0)
root.config(menu=menubar)
def cb(*args):
root.tk.call('::tk::TraverseToMenu', root, 'S')
tk.Button(root, text="Test", command=cb).pack()
root.mainloop()
我也试过update_idletasks()
没用。我该如何解决这个问题?
尝试过:
Windows7, Python 3.6, Tkinter 8.6.
Tkinter 按钮不应该那样工作; 这就是 menubutton 的用途。 但是如果您要继续破解按钮,则需要绑定到按钮上的事件,而不仅仅是使用命令回调(当按钮被激活时,鼠标按钮 1 在按钮上释放时被触发;当 mouse-button-1 在按钮上被按下时发生激活。
我真的建议改用菜单按钮 (tk.Menubutton
)。对用户来说更容易,因为它的设计看起来就像按下时post一个菜单。
琐事
这个技巧适用于 X Window 系统(读作 Unix),因为 "Alt-keying" 由 tk
本身通过 tk::TraverseToMenu
函数处理,它绑定到在这种情况下 all
绑定标签。
在你的情况下 tk
检测到它在 Win
环境中工作,并且仅将 tk::TraverseToMenu
函数绑定到 Menubutton
绑定标签,因为在这种情况下"Alt-keying" 由原生 Win
wm 处理。
所说的内容由menu.tcl
中的源代码表示:
if {[tk windowingsystem] eq "x11"} {
bind all <Alt-KeyPress> {
tk::TraverseToMenu %W %A
}
bind all <F10> {
tk::FirstMenu %W
}
} else {
bind Menubutton <Alt-KeyPress> {
tk::TraverseToMenu %W %A
}
bind Menubutton <F10> {
tk::FirstMenu %W
}
}
解决方案
当您按下 Alt 键时,Windows sends a message, which signaling that the Alt-key is pressed down, and waits for another message,其中包含指定的字符作为 ANSI 代码。
接收到指定字符后,wm 将尝试找到要打开的菜单。
同时 tk::TraverseToMenu
效果很好 - 尝试将空字符串或任意字符作为 char
参数传递,但找不到菜单。只有当您尝试在 Windows 房子附近的草坪上玩耍时才会出现此问题。
您在这种情况下的最佳选择:SendMessage or keybd_event。
所以一个完整的 hack(如@Donal Fellows 所说)是这样的:
import tkinter as tk
root = tk.Tk()
if root._windowingsystem == 'win32':
import ctypes
keybd_event = ctypes.windll.user32.keybd_event
alt_key = 0x12
key_up = 0x0002
def traverse_to_menu(key=''):
if key:
ansi_key = ord(key.upper())
# press alt + key
keybd_event(alt_key, 0, 0, 0)
keybd_event(ansi_key, 0, 0, 0)
# release alt + key
keybd_event(ansi_key, 0, key_up, 0)
keybd_event(alt_key, 0, key_up, 0)
else:
# root._windowingsystem == 'x11'
def traverse_to_menu(key=''):
root.tk.call('tk::TraverseToMenu', root, key)
menubar = tk.Menu(root)
sub1 = tk.Menu(menubar, tearoff=0)
sub1.add_command(label='Item 1', command=lambda: print('item 1'))
sub1.add_command(label='Item 2', command=lambda: print('item 2'))
menubar.add_cascade(menu=sub1, label='Sub1', underline=0)
root.config(menu=menubar)
traverse_button = tk.Button(root, text='Test', command=lambda: traverse_to_menu('S'))
traverse_button.pack()
root.mainloop()
跟进 sub1
,当按钮 Test
是按下,但我不能。在以下代码中,按钮似乎冻结了 GUI:
import tkinter as tk
root = tk.Tk()
menubar = tk.Menu(root)
sub1 = tk.Menu(menubar, tearoff=0)
sub1.add_command(label="Item 1", command=lambda : print("item 1"))
sub1.add_command(label="Item 2", command=lambda : print("item 2"))
menubar.add_cascade(menu=sub1, label="Sub1", underline=0)
root.config(menu=menubar)
def cb(*args):
root.tk.call('::tk::TraverseToMenu', root, 'S')
tk.Button(root, text="Test", command=cb).pack()
root.mainloop()
我也试过update_idletasks()
没用。我该如何解决这个问题?
尝试过:
Windows7, Python 3.6, Tkinter 8.6.
Tkinter 按钮不应该那样工作; 这就是 menubutton 的用途。 但是如果您要继续破解按钮,则需要绑定到按钮上的事件,而不仅仅是使用命令回调(当按钮被激活时,鼠标按钮 1 在按钮上释放时被触发;当 mouse-button-1 在按钮上被按下时发生激活。
我真的建议改用菜单按钮 (tk.Menubutton
)。对用户来说更容易,因为它的设计看起来就像按下时post一个菜单。
琐事
这个技巧适用于 X Window 系统(读作 Unix),因为 "Alt-keying" 由 tk
本身通过 tk::TraverseToMenu
函数处理,它绑定到在这种情况下 all
绑定标签。
在你的情况下 tk
检测到它在 Win
环境中工作,并且仅将 tk::TraverseToMenu
函数绑定到 Menubutton
绑定标签,因为在这种情况下"Alt-keying" 由原生 Win
wm 处理。
所说的内容由menu.tcl
中的源代码表示:
if {[tk windowingsystem] eq "x11"} {
bind all <Alt-KeyPress> {
tk::TraverseToMenu %W %A
}
bind all <F10> {
tk::FirstMenu %W
}
} else {
bind Menubutton <Alt-KeyPress> {
tk::TraverseToMenu %W %A
}
bind Menubutton <F10> {
tk::FirstMenu %W
}
}
解决方案
当您按下 Alt 键时,Windows sends a message, which signaling that the Alt-key is pressed down, and waits for another message,其中包含指定的字符作为 ANSI 代码。 接收到指定字符后,wm 将尝试找到要打开的菜单。
同时 tk::TraverseToMenu
效果很好 - 尝试将空字符串或任意字符作为 char
参数传递,但找不到菜单。只有当您尝试在 Windows 房子附近的草坪上玩耍时才会出现此问题。
您在这种情况下的最佳选择:SendMessage or keybd_event。
所以一个完整的 hack(如@Donal Fellows 所说)是这样的:
import tkinter as tk
root = tk.Tk()
if root._windowingsystem == 'win32':
import ctypes
keybd_event = ctypes.windll.user32.keybd_event
alt_key = 0x12
key_up = 0x0002
def traverse_to_menu(key=''):
if key:
ansi_key = ord(key.upper())
# press alt + key
keybd_event(alt_key, 0, 0, 0)
keybd_event(ansi_key, 0, 0, 0)
# release alt + key
keybd_event(ansi_key, 0, key_up, 0)
keybd_event(alt_key, 0, key_up, 0)
else:
# root._windowingsystem == 'x11'
def traverse_to_menu(key=''):
root.tk.call('tk::TraverseToMenu', root, key)
menubar = tk.Menu(root)
sub1 = tk.Menu(menubar, tearoff=0)
sub1.add_command(label='Item 1', command=lambda: print('item 1'))
sub1.add_command(label='Item 2', command=lambda: print('item 2'))
menubar.add_cascade(menu=sub1, label='Sub1', underline=0)
root.config(menu=menubar)
traverse_button = tk.Button(root, text='Test', command=lambda: traverse_to_menu('S'))
traverse_button.pack()
root.mainloop()