前言
上文写了Python多线程中的通讯,这里我们说一下Qt使用PySide2开发时多线程之间的处理方式。
信号机制(推荐)
信号及处理类
main_signal_obj.py
1 | import threading |
全局变量
global_vars.py
1 | # 共享模块 |
初始化变量
1 | import sys |
分线程发送信号
1 | def run_async_task(): |
信号连接类型
1 | self.installAppSignal.connect(self.update_app, Qt.AutoConnection) |
在 PySide2(以及 Qt 框架)中,信号关联的槽函数执行所在的线程并非由创建关联时所在的线程决定,而是由连接类型(connection type)来决定。
下面为你详细介绍不同连接类型下槽函数的执行线程:
Qt.DirectConnection
当使用
Qt.DirectConnection
连接类型时,槽函数会在发射信号的线程中立即执行。也就是说,槽函数的执行线程和发射信号的线程是相同的。Qt.QueuedConnection
若使用
Qt.QueuedConnection
连接类型,槽函数会在接收对象所在的线程的事件循环中执行(也就是定义槽函数的那个对象创建时所在的线程)。通常,接收对象所在的线程就是创建该对象的线程。
对于 GUI 相关的对象,一般是主线程。
Qt.BlockingQueuedConnection
Qt.BlockingQueuedConnection
和Qt.QueuedConnection
类似,槽函数会在接收对象所在的线程的事件循环中执行。不过,发射信号的线程会被阻塞,直到槽函数执行完毕。
Qt.AutoConnection
(默认连接类型)若使用
Qt.AutoConnection
,Qt 会依据发射信号的线程和接收对象所在的线程来自动选择Qt.DirectConnection
或者Qt.QueuedConnection
。如果发射信号的线程和接收对象所在的线程相同,就使用
Qt.DirectConnection
;否则,使用
Qt.QueuedConnection
。
综上所述,信号关联的槽函数执行所在的线程由连接类型和接收对象所在的线程共同决定,而非创建关联时所在的线程。
QMetaObject.invokeMethod()
QMetaObject.invokeMethod()
是一个强大的工具,允许在不同线程中安全地调用方法。
不建议使用这种方法,使用字符串传递的方法名在代码排查的时候不方便。
语法
1 | QMetaObject.invokeMethod(object, methodName, connectionType, *args) |
参数
object
: 要调用方法的对象。object
参数通常是QObject
的子类实例。methodName
: 要调用的方法名,字符串形式。对应的方法要添加@Slot()
注解connectionType
: 连接类型,通常是Qt.DirectConnection
或Qt.QueuedConnection
。Qt.DirectConnection
: 直接调用方法,通常在当前线程中执行。Qt.QueuedConnection
: 方法调用会被放入主线程的事件队列中,通常用于从其他线程调用主线程的方法,确保线程安全。
*args
: 传递给方法的参数。
示例
被调用方法的类
1 | class MainObj(QObject): |
分线程运行的类
1 | class BackgroundWorker(threading.Thread): |
全局变量
1 | class GlobalVars: |
分线程调用主线程方法
1 | from PySide2.QtCore import (QMetaObject) |
主进程
1 | import sys |
获取所在线程
1 | import threading |