如何跨线程发送无信号?

时间:2016-03-01 01:06:55

标签: python multithreading pyqt pyside signals-slots

我已经实现了Qt Threading docs

中描述的工作模式的一个版本

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

定义Signal时,我将参数签名类型设置为object,因为我认为它应该允许我通过Signal传递任何python对象。

result_ready = QtCore.Signal(object)

然而,当我尝试通过None传递Signal时,它会崩溃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_()

2 个答案:

答案 0 :(得分:2)

这看起来像是一个PySide错误。相同的示例代码与PyQt4完全一样。

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

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

如果你想解决这个问题,我想你可以发布你自己的哨兵对象作为None的占位符。

<强>更新

发现了2012年3月发布的错误PYSIDE-17!可悲的是,建议的补丁似乎从未被审查过。

答案 1 :(得分:1)

如果你改变信号到适当的班级,那么它对我来说很好。例如:

result_ready = QtCore.Signal(str)

这适用于两种情况。

根据文件

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

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