子菜单项不调用函数[带有效解决方案]
#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 找到了一个解决办法。
我们想要的功能是:
- 对
"activate"
信号做出反应(支持键盘导航和父菜单项被点击的情况) - 对
"button-press-event"
信号做出反应(以解决之前提到的 bug) - 在下一个主循环迭代中运行回调函数(避免阻塞 X 服务器)
- 确保回调函数只被调用一次(因为
"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"
的变通方法。