【问题标题】:How to send None with Signals across threads?如何跨线程发送无信号?
【发布时间】:2016-06-13 06:40:45
【问题描述】:

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

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

在定义Signal 时,我将参数签名类型设置为object,因为我相信它应该允许我通过Signal 传递任何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_()

【问题讨论】:

    标签: python multithreading pyqt pyside signals-slots


    【解决方案1】:

    这看起来像一个 PySide 错误。使用 PyQt4 时,相同的示例代码完全符合预期。

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

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

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

    更新

    发现错误,PYSIDE-17,发布于 2012 年 3 月!遗憾的是,建议的补丁似乎从未经过审核。

    【讨论】:

      【解决方案2】:

      如果您将信号更改为适当的类,那么它对我来说很好。例如:

      result_ready = QtCore.Signal(str)
      

      这两种情况都有效。

      根据文档

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

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

      【讨论】:

      • 是的,但是如果您使用 OP 实际尝试发出的对象的类型(即type(None)),它将不起作用。
      猜你喜欢
      • 2015-08-21
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 2017-05-10
      相关资源
      最近更新 更多