尝试显示小部件时出现线程问题

Thread issue when trying to show a widget

我正在尝试创建一个启动器(例如 Albert 或 Spotlight)。为此,我需要将快捷方式连接到 window 的 show() 函数。为此,我正在使用 keyboard 库。

我在这里:

import sys
from PySide import QtGui
import keyboard


class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Example')

def main():
    app = QtGui.QApplication(sys.argv)

    window = Example()
    keyboard.add_hotkey('ctrl+alt+9', window.show, args=[])

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

但是在调用快捷方式时,出现以下 Qt 错误:

QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread

有人知道这可能是什么原因吗?

消息表明问题是回调是从另一个线程调用的,而在 Qt 中,GUI 不能从另一个线程更新,一个可能的解决方案是创建一个 class 来提供连接信号显示,并且该信号作为回调发出。

import sys
import keyboard
from PySide import QtCore, QtGui


class SignalHelper(QtCore.QObject):
    signal = QtCore.Signal()


class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Example')


def main():
    app = QtGui.QApplication(sys.argv)
    window = Example()
    helper = SignalHelper()
    helper.signal.connect(window.show)
    keyboard.add_hotkey('ctrl+alt+9', helper.signal.emit)
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

对于这些情况,更好的选择是将 QMetaObject::invokeMethod()Qt::QueuedConnection 结合使用,因为 show() 是一个插槽,如下所示:

import sys
import keyboard
from PySide import QtCore, QtGui


class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Example')


def main():
    app = QtGui.QApplication(sys.argv)
    window = Example()
    keyboard.add_hotkey('ctrl+alt+9', 
        QtCore.QMetaObject.invokeMethod, 
        args=(window, "show", QtCore.Qt.QueuedConnection))
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()