如何跨线程发送 None 信号?

How to send None with Signals across threads?

我已经实现了 Qt Threading docs 中描述的工作模式版本。

我正在使用 Signals/Slots 在工作线程和主线程之间发送数据。

定义 Signal 时,我将参数签名类型设置为 object 因为我相信它应该允许我通过 [=14= 传递任何 python 对象].

result_ready = QtCore.Signal(object)

但是,当我尝试通过 Signal 传递 None 时,它崩溃了 python。这仅在尝试跨线程传递 Signal 时发生。如果我注释掉 self.worker.moveToThread(self.thread) 行,它会起作用并且 None 成功通过 Signal.

为什么在这种情况下我无法通过 None

我正在使用 PySide 1.2.2Qt 4.8.5

import sys

from PySide import QtCore, QtGui


class Worker(QtCore.QObject):

    result_ready = QtCore.Signal(object)

    @QtCore.Slot()
    def work(self):
        print 'In Worker'

        # This works
        self.result_ready.emit('Value')

        # This causes python to crash
        self.result_ready.emit(None)


class Main(QtGui.QWidget):

    def __init__(self):
        super(Main, self).__init__()
        self.ui_lay = QtGui.QVBoxLayout()
        self.setLayout(self.ui_lay)
        self.ui_btn = QtGui.QPushButton('Test', self)
        self.ui_lay.addWidget(self.ui_btn)
        self.ui_lay.addStretch()
        self.setGeometry(400, 400, 400, 400)
        self.worker = Worker()
        self.thread = QtCore.QThread(self)
        self.worker.moveToThread(self.thread)
        self.thread.start()
        self.ui_btn.clicked.connect(self.worker.work)
        self.worker.result_ready.connect(self.handle_worker_result)

    @QtCore.Slot(object)
    def handle_worker_result(self, result=None):
        print 'Handling output', result

    def closeEvent(self, event):
        self.thread.quit()
        self.thread.wait()
        super(Main, self).closeEvent(event)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    obj = Main()
    obj.show()
    app.exec_()

如果您将信号更改为正确的 class,那么对我来说效果很好。例如:

result_ready = QtCore.Signal(str)

这两种情况都适用。

根据文档

可以使用 QtCore.Signal() class 定义信号。 Python 类型和 C 类型可以作为参数传递给它。如果您需要重载,只需将类型作为元组或列表传递即可。

https://wiki.qt.io/Signals_and_Slots_in_PySide#Using_QtCore.Signal.28.29

这看起来像是 PySide 错误。相同的示例代码在 PyQt4 中完全符合预期。

问题出在 type of signal connection。对于跨线程信号,这将使用 QueuedConnection 除非您另有说明。如果在示例代码中将连接类型更改为 DirectConnection,它将按预期工作 - 但当然它不再是线程安全的。

A QueuedConnection 将 post 事件发送到接收线程的事件队列。但为了使其成为线程安全的,Qt 必须序列化发出的参数。然而,PySide 显然需要在这里注入一些魔法来处理 Qt 不知道的 python 类型。如果我不得不猜测,我敢打赌 PySide 错误地将 python None 对象转换为 C++ NULL 指针,这显然会在以后产生严重后果。

如果您想解决这个问题,我想您可以发出自己的哨兵对象作为 None.

的占位符

更新:

发现了 2012 年 3 月 post 编辑的错误 PYSIDE-17!遗憾的是,建议的补丁似乎从未被审查过。