Контекстное меню PyQt5 SystemTrayIcon не принимает нажатия клавиш при программном запуске

#python #pyqt5 #archlinux #dbus

#python #pyqt5 #archlinux #dbus

Вопрос:

У меня есть скрипт на python, который использует PyQt5 для размещения значка в системном трее, который я могу использовать для запуска других скриптов.

Он отлично подходит для обработки нескольких сценариев, и, поскольку я не использую их часто, нет смысла назначать сочетания клавиш для всех этих сценариев.

Пока я использую щелчок правой кнопкой мыши для открытия контекстного меню, все в порядке, но я также хотел бы вызвать его с помощью нажатий клавиш. Проблема, с которой я столкнулся, заключается в том, что я не нажимаю на значок, даже если отображается меню, фокус отсутствует, поэтому любые другие нажатия клавиш туда не попадают.

Часть для запуска контекстного меню:

     @QtCore.pyqtSlot(QDBusMessage)
    def dmsg(self, message):
        menu = self.icon.contextMenu()
        if menu.isVisible():
            menu.close()
        else:
            menu.exec(QtCore.QPoint(0,0))
            # menu.show()
 

Я могу использовать dbus для отправки сигнала, который запускает эту функцию, и ContextMenu можно увидеть, не нажимая значок в системном трее.

Я использую dbus-send --system / gaurav.menu.toggle для отправки сигнала для переключения видимости контекстного меню, и это работает.

Воспроизводимый пример выглядит следующим образом:

 from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtDBus import QDBusConnection, QDBusMessage
import sys

def exe_script(name):
    action = name.text()
    if action == 'amp;Exit':
        sys.exit(0)
    elif action == 'amp;Hello':
        print('hello')

def read_actions():
    return 

def get_menu(all_actions):
    menu = QtWidgets.QMenu()
    actions = []
    for name in ['amp;Exit', 'amp;Hello']:
        mi = menu.addAction(name)
        actions.append(mi)
    menu.triggered.connect(lambda q:exe_script(q))
    return menu,actions



class QDBhandler(QtCore.QObject):
    def __init__(self,icon):
        super(QDBhandler,self).__init__()
        bus = QDBusConnection.systemBus()
        bus.registerObject('/', self)
        bus.connect(
            '',
            '/',
            'gaurav.menu',
            'toggle',
            self.dmsg
        )
        self.icon = icon

    @QtCore.pyqtSlot(QDBusMessage)
    def dmsg(self, message):
        menu = self.icon.contextMenu()
        if menu.isVisible():
            menu.close()
        else:
            menu.exec(QtCore.QPoint(0,0))
            # menu.show()
        

def main():
    app = QtWidgets.QApplication(sys.argv)
    icon = QtWidgets.QSystemTrayIcon()
    handler = QDBhandler(icon)
    icon.setToolTip('Execute Scripts')
    actions_dict = read_actions()
    menu,actions = get_menu(actions_dict)
    icon.setContextMenu(menu)
    icon.show()
    sys.exit(app.exec())
    

if __name__ == '__main__':
    main()

 

Системная информация:
Archlinux, i3-диспетчер окон gaps

Комментарии:

1. Я не могу воспроизвести проблему при запуске команды dbus, но ранее я сталкивался с аналогичной проблемой при использовании сочетания клавиш, и это связано с управлением фокусом клавиатуры диспетчера окон: иногда меню не получает фокус должным образом (обычно, если оно появляется, когда клавиша еще не отпущена,и предыдущий объект X все еще «захватывает» клавиатуру). Кажется, что Qt обходит это внутренне, освобождая фокус при запуске с указанием Context причины, но на данный момент я все еще не могу избежать этого на 100%.

2. Когда вы запускаете это и отправляете этот триггер dbus, он работает на вашем (например, отправьте сигнал dbus, затем нажмите E для выхода.)? Как вы сказали, мои ключи захватываются любым окном, в котором я был перед отправкой этого сигнала dbus. Есть ли способ самостоятельно запустить этот контекст? Я попытался вызвать триггер с помощью Reason, но он не позволяет мне его вызывать.

3. К сожалению, сигнал запуска передается внутренне с помощью (частного) помощника системной платформы, вызов его вручную вам не поможет. Я знаю только две альтернативы, и ни одна из них на самом деле не очень хороша: углубиться в привязки X, чтобы «очистить» текущий захват клавиатуры, или создать поддельное событие щелчка мыши (с помощью qTest или внешнего инструмента) для эмуляции кнопки мыши.

4. Ну, если мне действительно нужно зайти так далеко, лучше создать виджет / окно вместо контекстного меню. Таким образом, он определенно получит фокус при запуске. Вероятно, мне следует использовать xlib / sdl2, чтобы сделать это на C, чтобы оно было быстрее и потребляло меньше памяти. Я думал, что у него будет простое решение, и было бы проще что-то изменить, если сделать это на python.

5. Технически это просто, проблема возникает при решении некоторых системных проблем, подобных этим (технически это «не ошибка» Qt, но это связано с тем, как ведет себя wm и как Qt создает новые виджеты). Обратите внимание, что с PyGTK это возможно, поскольку оно предоставляет глобальные ярлыки (которые недоступны в Qt) с использованием модуля keybinder, а фокус клавиатуры правильно отображается во всплывающем окне.