tkinter:在windows上使用带有菜单的自定义事件

ldxq2e6h  于 2021-07-13  发布在  Java
关注(0)|答案(2)|浏览(302)

使用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 这不是主要的应用程序)。

5us2dqdw

5us2dqdw1#

菜单由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>>')
        )
        ...
xam8gpfp

xam8gpfp2#

我想我知道了,但不是100%确定。
看看这个代码:

import tkinter as tk

def trying_to_reach(event):
    print("!")

root = tk.Tk()

text = tk.Text(root)
text.pack()

text.bind("<<Foo>>", trying_to_reach)
root.after(100, text.event_generate, "<<Foo>>")

root.mainloop()

代码运行100毫秒后, ! 出现在屏幕上,但我对 text.pack() ,屏幕上不显示任何内容。如果我改变了 root.after(100, text.event_generate, "<<Foo>>")text.event_generate("<<Foo>>") ,屏幕上不显示任何内容。这证明了@bryanoakley的理论,即它与widget的可见性有关。这意味着问题很可能出在 tcl /功能 tcl 电话。
解决方案是使用一个虚拟小部件,或者像@matiiss建议的那样使用它 root.event_generate 以及 root.bind .

相关问题