Хотя "button-press-event"
работает, у него есть несколько недостатков:
- Не работает для навигации только с клавиатуры
- Если обратный вызов блокируется, весь X-серверзаблокирован (см. ошибка gPodder 1778 )
Для моего собственного приложения (gPodder) я работал над этим в commit a09b204a .
Что мы хотим:
- Реагировать на сигнал
"activate"
(для навигации с помощью клавиатуры и в ситуациях, когда также нажимается родительский элемент меню) - Реагировать на
"button-press-event"
сигнал (чтобы обойти ошибку) - Запустить обратный вызов в следующей итерации основного цикла (чтобы избежать блокировки 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)
Также, ошибка 695488 в GNOME Bugzilla.