前言
上文写了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.BlockingQueuedConnectionQt.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  |