前言
上一篇文章介绍了在PySide6中使用多线程去解决PySide6/PyQT的界面卡死问题,这次来具体介绍下多线程在使用上的一些细节。
本文尝试对以下两个问题进行解决:
- 对 PySide6/PyQT 多线程的使用不熟悉;
- 在 PySide6/PyQT 的应用程序里有耗时任务,导致应用程序卡死;
值得注意的是在 PySide6/PyQT 中实现多线程,可以选择的有很多,
有 QObject、QThread、QRunnable、QtConcurrent 等;
但是它们在使用上都大差不差,所以在本篇文章中,挑选了 QThread 来讲解。
知识点📖📖
本文用到的几个PySide6的知识点及链接。
作用 | 链接 |
---|---|
创建新线程 | QThread |
对象间通信的机制,允许对象发送和接收信号 | Signal |
用于响应Signal信号的方法 | Slot |
实现
QThread 多线程
使用注意事项
在使用 PySide6 的 QThread 时,注意事项有以下几点:
- 不要使用Python原生的线程库来实现;
- 消息或任务结果 通过Signal信号来传递;
- 不要在QThread调用主线程的GUI控件,应用程序会进入卡死状态;
- QThread对象必须在主线程中创建,否则程序可能会奔溃;
- 因为PySide6是基于事件循环的框架,GUI线程和子线程都运行在同一个事件循环中。如果在子线程中创建和启动QThread对象,它会尝试创建一个新的事件循环,这会导致两个事件循环并行运行,产生无法预估的结果。
代码
代码用上一篇文章中的,看这里 —《解决PySide6/PyQT的界面卡死问题(PySide6/PyQT多线程》
# -*- coding: utf-8 -*-
import time
import requests
from PySide6.QtCore import (QThread, Signal, Slot, QSize)
from PySide6.QtWidgets import (QApplication, QPushButton, QLabel, QVBoxLayout, QWidget)
class MyThread(QThread):
signal_tuple = Signal(tuple)
def __init__(self, func, *args, **kwargs):
super().__init__()
self.func = func
self.args = args
self.count: int = kwargs.get('count')
def run(self):
for idx in range(1, self.count + 1):
result = self.func(*self.args)
time.sleep(1)
# 任务完成后发出信号
self.signal_tuple.emit((idx, result))
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setup_ui()
#
self.button.clicked.connect(self.setup_thread)
def setup_ui(self):
self.setWindowTitle('demo')
self.resize(QSize(250, 180))
# 创建一个垂直布局
layout = QVBoxLayout()
# 创建一个标签
self.label = QLabel('This is a label => ')
layout.addWidget(self.label)
# 创建一个按钮
self.button = QPushButton('Send Request')
layout.addWidget(self.button)
# 将布局设置为主窗口的布局
self.setLayout(layout)
# 显示窗口
self.show()
def setup_thread(self):
self.thread_ = MyThread(self.send_request,
count=10)
self.thread_.signal_tuple.connect(self.thread_finished)
self.thread_.start()
def send_request(self):
return requests.get('https://www.csdn.net/').text[:15]
@Slot(tuple)
def thread_finished(self, item):
self.label.setText('This is a label => ' + str(item))
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
代码释义
MyTherad类
- 继承了QThread;
- 创建了一个 signal_tuple信号;
- 类接收一个func函数以及不定长的传参(这里主要传func 和count;
- 重写 run方法,线程的 start() 方法被调用时,就会自动执行run方法;
- 执行 count 次数的func,每次睡眠1秒,使用signal_tuple 将执行结果和执行次数发送出去。
MainWindow类
- 继承了 QWidget,实现了包含一个按钮和一个标签的简单窗口;
- 在setup_thread 函数中,实例化了MyThread,并将实例化后的signal_tuple信号连接到 thread_finished函数;
- thread_finished为槽函数,当signal_tuple发出信号时,这Slot(槽函数)将被调用,并修改label 的显示。
在实例代码 以及 代码释义中可以清晰的看到,示例代码完美符合了本文中我指出来的 QThread 多线程的几个注意事项:
- 使用Signal信号传递运行结果;
- 在主线程中创新子线程;
- 不在子线程中修改主线程的控件。
以上便是 QThread 多线程在 PySide6/PyQT 中的使用。
后话
本次分享到此结束,
see you~~🐱🏍🐱🏍