当按下按钮时如何查看菜单?

3

针对这个问题的后续,我正在尝试在按下按钮Test时查看(与使用左键单击相同)菜单sub1,但是我无法实现。在以下代码中,按钮似乎会冻结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。


2
你的代码在我的电脑上(Python3,Debian)可以运行。你用的是什么操作系统? - Novel
1
你看过这个吗?(链接为 https://dev59.com/SqDia4cB1Zd3GeqPJ9Nv ) - Novel
2个回答

1
Tkinter按钮不应该这样工作; 这就是菜单按钮的作用。但是,如果你要继续对一个按钮进行操作,你需要绑定按钮上的事件,而不仅仅是使用命令回调函数(当鼠标按钮1在按钮上释放时,该函数会触发;当鼠标按钮1在按钮上按下时,按钮会被激活)。
我真的建议使用菜单按钮(tk.Menubutton)。它对用户更容易,因为它被设计为在按下时将弹出菜单。

1

小知识

这个技巧在 X Window System(也称为 Unix)中适用,因为“Alt+键”由 tk::TraverseToMenu 函数处理,而该函数绑定在 all bind-tag 上。

而在您的情况下,tk 检测到它在 Win 环境下运行,并将 tk::TraverseToMenu 函数仅绑定到 Menubutton bind-tag 上,因为在这种情况下,“Alt+键”由本机的 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会发送一条消息,其中包含Alt键已被按下的信号,并等待另一条消息,其中包含指定字符的ANSI代码。 收到指定字符后,wm会尝试找到要打开的菜单。

同时,tk::TraverseToMenu运行良好 - 尝试将空字符串或任意字符作为char参数传递,无法找到菜单。只有当您尝试在Windows房子附近玩耍时才会出现问题。

在这种情况下,您最好的选择是:SendMessagekeybd_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()

非常好,非常感谢您的回答!有一件事我不明白,为什么您要定义函数而不是使用if/else语句来定义一个函数? - Nae
1
@Nae 这只是个人口味和我的习惯,将像导入这样的与操作系统相关的事物分开。真正的问题是为什么由tkinter生成的事件,如<Alt-v>没有效果,而<Alt-F4>则表现正常。 - CommonSense

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接