【问题标题】:Steal focus in PyQt5/Pyside2在 PyQt5/Pyside2 中窃取焦点
【发布时间】:2021-01-14 18:51:10
【问题描述】:

我正在创建一个启动器,样式为AlbertAlfreduLauncher。我的应用程序在后台运行,并在按下热键时显示。我使用pynput 来收听热键。我不能使用 PyQt5 热键的功能(不能吗?),因为我需要在系统范围内监听键盘事件,而不仅仅是应用程序范围内。

按下快捷方式时,它会调用我的小部件的 show() 方法。唯一的问题是,尽管使用了 raise_setFocusactivateWindow,但我无法将焦点重新放在窗口上。

我发现了一个(丑陋的)解决方法,它包括打开一个 QMessageBox(+ 调整它的外观使其不可见,但我没有把它放在示例代码中)然后立即关闭它。

当我在 Linux 上工作时,这种变通方法正在发挥作用,我已经准备好忘记它是多么丑陋,因为它完成了这项工作。但我切换到 Windows(我的应用程序也必须在其上运行),现在这个厚颜无耻的把戏似乎会导致我的应用程序冻结然后崩溃。业力?肯定的。

无论如何,如果我的应用程序无法获得焦点,我的应用程序将毫无用处,所以我要问两个问题,如果只解决一个问题,我会很高兴。 :)

  • 你知道为什么显示 QMessageBox 会导致崩溃吗?
  • 您知道其他方法可以让我的应用程序重新获得关注吗?

这是一个可以使用的示例代码。

非常感谢:)

编辑:我刚刚发现,即使停用 QMessageBox 解决方法,应用程序最终也会崩溃(在调用 5、20、30 次热键之后)。所以问题也可能在于我将快捷方式绑定到 GUI 的方式,我担心线程问题,但这超出了我的知识范围:/

import sys
from PyQt5.QtWidgets import QLineEdit, QApplication, QMessageBox
from PyQt5.QtCore import QSize, Qt, QEvent
from pynput import keyboard


class Launcher(QLineEdit):
    def __init__(self):
        super().__init__()
        self.setFixedSize(QSize(600, 50))
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setPlaceholderText('Search...')

        self.installEventFilter(self)

        self.set_shortcut('<ctrl>+`')

    def set_shortcut(self, shortcut):
        def for_canonical(f):
            return lambda k: f(listener.canonical(k))

        hotkey = keyboard.HotKey(
            keyboard.HotKey.parse(shortcut),
            self.wake_up)

        listener = keyboard.Listener(
                on_press=for_canonical(hotkey.press),
                on_release=for_canonical(hotkey.release))

        listener.start()

    def wake_up(self):
        print('Waking up')
        self.show()
        self.cheeky_focus_stealer()

    def cheeky_focus_stealer(self):
        self.setFocus()
        self.raise_()
        self.activateWindow()

        # Working of linux, but causes freeze/crash on Windows 10
        message_box = QMessageBox(self)
        message_box.show()
        message_box.hide()

    def eventFilter(self, obj, event):
        if obj is self and event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Escape:
                self.hide()
                return True

        return super().eventFilter(obj, event)


def main():
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)

    window = Launcher()
    window.show()

    app.exec_()


if __name__ == "__main__":
    main()

【问题讨论】:

    标签: python pyqt5 pyside2


    【解决方案1】:

    我发现了我的错误,因此我在此处发布了一段更新的代码,因为它可能对任何尝试将全局热键绑定到影响 GUI 的函数(也就是两个不同的线程通信)的人有所帮助。

    我的错误确实是将热键触发的操作直接绑定到我的show() 方法,这意味着pynput 侦听器线程将尝试与QApplication 通信。

    诀窍是使用pyqtSignal() 并要求它触发show() 方法。信号本身由热键触发。

    在以干净的方式执行此操作后,我的 cheeky_focus_stealer 再次工作,因为它是从 GUI 线程运行的。

    import sys
    from PyQt5.QtWidgets import QLineEdit, QApplication, QMessageBox
    from PyQt5.QtCore import QSize, Qt, QEvent, QObject, pyqtSignal
    from pynput import keyboard
    
    
    class Forwarder(QObject):
        signal = pyqtSignal()
    
    
    class Launcher(QLineEdit):
        def __init__(self):
            super().__init__()
            self.setFixedSize(QSize(600, 50))
            self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
            self.setPlaceholderText('Search...')
    
            self.installEventFilter(self)
    
            self.set_shortcut('<ctrl>+`')
    
        def set_shortcut(self, shortcut):
            # The forwarder must be parented to the Launcher
            forwarder = Forwarder(parent=self)
            forwarder.signal.connect(self.wake_up)
    
            def for_canonical(f):
                return lambda k: f(listener.canonical(k))
    
            hotkey = keyboard.HotKey(
                keyboard.HotKey.parse(shortcut),
                forwarder.signal.emit)
    
            listener = keyboard.Listener(
                    on_press=for_canonical(hotkey.press),
                    on_release=for_canonical(hotkey.release))
    
            listener.start()
    
        def wake_up(self):
            print('Waking up')
            self.show()
            self.cheeky_focus_stealer()
    
        def cheeky_focus_stealer(self):
            self.setFocus()
            self.raise_()
            self.activateWindow()
    
            # Working of linux, but causes freeze/crash on Windows 10
            message_box = QMessageBox(self)
            message_box.show()
            message_box.hide()
    
        def eventFilter(self, obj, event):
            if obj is self and event.type() == QEvent.KeyPress:
                if event.key() == Qt.Key_Escape:
                    self.hide()
                    return True
    
            return super().eventFilter(obj, event)
    
    
    def main():
        app = QApplication(sys.argv)
        app.setQuitOnLastWindowClosed(False)
    
        window = Launcher()
        window.show()
    
        app.exec_()
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

    • 非常感谢,我自己永远也找不到答案
    猜你喜欢
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多