使用 QObject 从 Python 线程发出信号

2024-05-11

我想知道与 QThread 相比,从 QObject 中的常规 python 线程发出信号会产生什么后果。

请参阅以下课程:

class MyObject(QtCore.QObject):

    def __init__(self):
        super().__init__()

    sig = pyqtSignal()

    def start(self):
        self._thread = Thread(target=self.run)
        self._thread.start()

    def run(self):
        self.sig.emit()
        # Do something

现在,假设在 GUI 线程中,我有:

def __init__(self):
    self.obj = MyObject()
    self.obj.sig.connect(self.slot)
    self.obj.start()

def slot(self):
    # Do something

the slot确实是在信号发出时执行的。但是,我想知道哪个线程将slot方法被执行在?如果我使用的话会有什么不同吗QThread而不是 python 线程MyObject?

我正在使用 PyQt5 和 Python 3。


默认情况下,Qt自动对信号进行排队 https://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads当它们跨线程发出时。为此,它会序列化信号参数,然后将事件发布到接收线程的事件队列,最终将执行任何连接的槽。因此,以这种方式发出的信号保证是线程安全的。

对于外螺纹,Qt 文档状态 https://doc.qt.io/qt-5/threads.html下列:

注意:Qt的线程类是用原生线程实现的 蜜蜂;例如,Win32 和 pthreads。因此,它们可以与 相同本机 API 的线程。

一般来说,如果文档声明 QtAPI是线程安全的 https://doc.qt.io/qt-5/threads-reentrancy.html,该保证适用于使用同一本机库创建的所有线程 - 而不仅仅是 Qt 本身创建的线程。这意味着使用线程安全 API 显式地将事件发布到其他线程也是安全的,例如postEvent() https://doc.qt.io/qt-5/qcoreapplication.html#postEvent and invoke() https://doc.qt.io/qt-5/qmetamethod.html#invoke.

因此,使用之间没有真正的区别threading.Thread and QThread当涉及到发出跨线程信号时,只要 Python 和 Qt 使用相同的底层本机线程库。这表明更喜欢使用的一个可能原因QThread在 PyQt 应用程序中是可移植性,因为这样就不会存在混合不兼容的线程实现的危险。然而,鉴于 Python 和 Qt 都被故意设计为跨平台,因此在实践中这个问题不太可能出现。


至于哪个线程的问题slot将在 - 对于 Python 和 Qt 而言,它将在main线。相比之下,run方法将在worker线。在 Qt 应用程序中进行多线程处理时,这是一个非常重要的考虑因素,因为在主线程之外执行 gui 操作是不安全的。使用信号可以让您在工作线程和 gui 之间安全地进行通信,因为连接到从工作线程发出的信号的插槽将在主线程中被调用,从而允许您在必要时更新 gui。

下面是一个简单的脚本,显示了每个方法在哪个线程中调用:

import sys, time, threading
from PyQt5 import QtCore, QtWidgets

def thread_info(msg):
    print(msg, int(QtCore.QThread.currentThreadId()),
          threading.current_thread().name)

class PyThreadObject(QtCore.QObject):
    sig = QtCore.pyqtSignal()

    def start(self):
        self._thread = threading.Thread(target=self.run)
        self._thread.start()

    def run(self):
        time.sleep(1)
        thread_info('py:run')
        self.sig.emit()

class QtThreadObject(QtCore.QThread):
    sig = QtCore.pyqtSignal()

    def run(self):
        time.sleep(1)
        thread_info('qt:run')
        self.sig.emit()

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.pyobj = PyThreadObject()
        self.pyobj.sig.connect(self.pyslot)
        self.pyobj.start()
        self.qtobj = QtThreadObject()
        self.qtobj.sig.connect(self.qtslot)
        self.qtobj.start()

    def pyslot(self):
        thread_info('py:slot')

    def qtslot(self):
        thread_info('qt:slot')

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    thread_info('main')
    sys.exit(app.exec_())

Output:

main 140300376593728 MainThread
py:run 140299947104000 Thread-1
py:slot 140300376593728 MainThread
qt:run 140299871450880 Dummy-2
qt:slot 140300376593728 MainThread
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 QObject 从 Python 线程发出信号 的相关文章

随机推荐