子菜单项不调用函数[带有效解决方案]

4 投票
2 回答
1378 浏览
提问于 2025-04-16 13:10
    #submenu
    clearMenu = gtk.Menu()

    item = gtk.MenuItem("submenu item")
    item.connect("activate", lambda w: self.callBackFunction())
    clearMenu.append(item)
    item.show()


    '''TOP level'''
    menu = gtk.Menu()

    item = gtk.ImageMenuItem("Item1")
    img = gtk.Image()
    img.set_from_file('image1.png')
    item.set_image(img)
    menu.append(item)
    item.set_submenu(clearMenu) #attach submenu
    item.show()

    item = gtk.ImageMenuItem("Item2")
    img = gtk.Image()
    img.set_from_file('image2.png')
    item.set_image(img)   
    item.connect("activate", lambda w: self.callBackFunction())
    menu.append(item)
    item.show()

我的顶级项目“Item2”调用了定义好的函数“callBackFunction”。但是为什么“子菜单项”这样做呢?我哪里做错了?


编辑

这是我如何强制子菜单项执行我想要的操作的方法:

item.connect("button-press-event", self.callBackFunction, argument1, argument2)

但我还是不明白为什么事件“activate”在子菜单项上不起作用,而在顶级菜单项上却可以。

2 个回答

5

虽然 "button-press-event" 这个事件可以用,但它有一些缺点:

  • 它不支持仅用键盘导航
  • 如果回调函数卡住了,整个 X 服务器也会被阻塞(可以参考 gPodder 的 bug 1778

在我自己的应用程序(gPodder)中,我通过 提交 a09b204a 找到了一个解决办法。

我们想要的功能是:

  1. "activate" 信号做出反应(支持键盘导航和父菜单项被点击的情况)
  2. "button-press-event" 信号做出反应(以解决之前提到的 bug)
  3. 在下一个主循环迭代中运行回调函数(避免阻塞 X 服务器)
  4. 确保回调函数只被调用一次(因为 "activate""button-press-event" 在某些情况下可能同时发生)

对于第 1 和第 2 点,我们可以简单地连接这两个信号。对于第 3 点,我们可以使用 gobject.idle_add()。对于第 4 点,我们可以使用 threading.Semaphore

这样就得到了以下代码:

import threading
import gobject

def submenu_item_connect_hack(menu_item, callback, *args_for_callback):
    only_once = threading.Semaphore(1)

    def handle_event(item, event=None):
        if only_once.acquire(False):
            gobject.idle_add(callback, *args_for_callback)

    menu_item.connect('button-press-event', handle_event)
    menu_item.connect('activate', handle_event)

现在你可以在代码中这样使用:不再调用以下任意一个:

item.connect("activate", lambda w: self.callBackFunction())
item.connect("button-press-event", self.callBackFunction, argument1, argument2)

而是调用这个:

submenu_item_connect_hack(item, self.callBackFunction, argument1, argument2)

另外,在 GNOME Bugzilla 中也提交了 bug 695488

4

这是一个关于子菜单焦点的问题,在这里有详细解释

子菜单在点击它所附属的菜单项之前是无法获得焦点的(即使当鼠标悬停在菜单项上时子菜单已经出现)。

简单来说,子菜单里的选项在父菜单项没有被点击之前是不会被激活的。

这就解释了为什么用键盘导航时似乎可以正常工作。

我已经在这个问题上摸索了超过一年,但我不知道有什么解决办法——只有你发现的"button-press-event"的变通方法。

撰写回答