前言
关于 PySide6/PyQT 多线程,正确地处理多线程编程并确保线程之间的同步和通信并不容易。
本文以一个示例代码为基础,介绍 PySide6/PyQT多线程的运用,展示如何创建和管理线程,以及如何实现线程之间的同步和通信。
设想这么一个场景:
- 在实际开发过程中,在涉及到长时间运行的计算任务时,用户可能希望能够暂停、恢复和结束线程的执行,以便更好地控制程序的行为;
- 如线程间的同步和通信、线程的暂停、恢复和结束等;
...
本专栏前面几篇文章,几乎覆盖了PySide6/PyQT 多线程编程开发中的100%,
在PySide6/PyQT中, QProgressBar 控件完美适配了本文的主旨。
于是乎,这篇文章就将前面几篇 PySide6/PyQT 的文章串起来,使用QProgressBar 组件写一个进度条相关的GUI工具,方便读者更加深入的去理解多线程的使用。
值得一提的是,本文的代码是基于下面这篇文章的示例代码。
- 点击直达: PySide6/PyQT多线程之 线程安全:互斥锁&条件变量的最佳实践
知识点📖📖
本文用到的几个PySide6的知识点及链接。
作用 | 链接 |
---|---|
创建新线程 | QThread |
对象间通信的机制,允许对象发送和接收信号 | Signal |
用于响应Signal信号的方法 | Slot |
线程同步机制,用于协调多个线程之间对共享资源的访问 | QMutex |
锁定互斥锁的对象,简化代码,避免手动处理锁的加锁和解锁操作 | QMutexLocker |
线程同步机制,一般配合 QMutex 使用 | QWaitCondition |
进度条控件 | QProgressBar |
实现
这里是对完整代码拆解再讲解,
创建线程
- 重写run()方法,该方法会在新线程启动时候执行;
- 新增两个方法,分别是暂停和恢复线程
class MyThread(QThread):
def __init__(self, parent=None):
super().__init__(parent=parent)
def pause_thread(self):
# 在新线程中执行的暂停的代码
def resume_thread(self):
# 在新线程中执行的恢复的代码
def run(self):
# 在新线程中执行的代码
启动线程
- 实例化类,再调用 start_thread() 启动线程
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setup_ui()
self.setup_thread()
def setup_ui(self):
# GUI界面绘制
def setup_thread(self):
self.thread = MyThread()
def start_thread(self):
self.thread.start()
def paused_thread(self):
self.thread.pause_thread()
def resume_thread(self):
self.thread.resume_thread()
def stop_thread(self):
self.thread.quit()
my_thread = MyThread()
my_thread
my_thread.start()
暂停线程
- 调用 pause_thread() 方法,就会暂停线程
self.thread.pause_thread()
恢复线程
- 调用 resume_thread() 方法,就会恢复线程运行
self.thread.resume_thread()
终止线程
- 调用 quit() 方法,就会停止线程
self.thread.quit()
完整代码
该代码实现了一个具有启动、暂停、恢复和终止功能的线程,并将进度值显示在进度条上。
# -*- coding: utf-8 -*-
# Name: demo.py
# Author: 小菜
# Date: 2023/5/4
# Description:
import sys
from PySide6.QtCore import (QThread, QWaitCondition, QMutex, Signal, QMutexLocker)
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QPushButton, QProgressBar, QApplication)
class MyThread(QThread):
valueChange = Signal(int)
def __init__(self, parent=None):
super().__init__(parent=parent)
self.is_paused = bool(0) # 标记线程是否暂停
self.progress_value = int(0) # 进度值
self.mutex = QMutex() # 互斥锁,用于线程同步
self.cond = QWaitCondition() # 等待条件,用于线程暂停和恢复
def pause_thread(self):
with QMutexLocker(self.mutex):
self.is_paused = True # 设置线程为暂停状态
def resume_thread(self):
if self.is_paused:
with QMutexLocker(self.mutex):
self.is_paused = False # 设置线程为非暂停状态
self.cond.wakeOne() # 唤醒一个等待的线程
def run(self):
while True:
with QMutexLocker(self.mutex):
while self.is_paused:
self.cond.wait(self.mutex) # 当线程暂停时,等待条件满足
if self.progress_value >= 100:
self.progress_value = 0
return # 当进度值达到 100 时,重置为 0 并退出线程
self.progress_value += 1
self.valueChange.emit(self.progress_value) # 发送进度值变化信号
self.msleep(30)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.thread_running = False # 标记线程是否正在运行
self.setup_ui()
self.setup_thread()
def setup_ui(self):
layout = QVBoxLayout(self)
self.progressBar = QProgressBar(self)
layout.addWidget(self.progressBar)
layout.addWidget(QPushButton(r'启动', self, clicked=self.start_thread))
layout.addWidget(QPushButton(r'停止', self, clicked=self.paused_thread))
layout.addWidget(QPushButton(r'恢复', self, clicked=self.resume_thread))
layout.addWidget(QPushButton(r'结束', self, clicked=self.stop_thread))
self.show()
def setup_thread(self):
self.thread = MyThread()
self.thread.valueChange.connect(self.progressBar.setValue)
self.thread_running = True
def start_thread(self):
if self.thread_running:
self.thread.start()
if not self.thread_running:
self.setup_thread()
self.thread.start()
def paused_thread(self):
if not self.thread_running:
return
if not self.thread.isRunning():
self.thread.start()
else:
self.thread.pause_thread()
def resume_thread(self):
if not self.thread_running:
return
self.thread.resume_thread()
def stop_thread(self):
self.thread.quit() # 终止线程的事件循环
self.thread_running = False # 标记线程停止
self.progressBar.setValue(0) # 重置进度条的值
if __name__ == '__main__':
app = QApplication()
window = MainWindow()
sys.exit(app.exec())
代码释义
MyThread 类
MyThread类继承自QThread,是一个自定义的线程类。它通过发射信号来通知界面更新进度条的值。
属性
- valueChange:自定义的信号,用于发送进度值变化的信号。
- is_paused:一个布尔值,用于标记线程是否暂停。
- progress_value:一个整数,表示进度值。
- mutex:QMutex对象,用于线程同步。
- cond:QWaitCondition对象,用于线程暂停和恢复。
方法
- __init__(self, parent=None):构造函数,用于初始化对象。
- pause_thread(self):暂停线程的方法。
- resume_thread(self):恢复线程的方法。
- run(self):线程执行的方法。在一个无限循环中,判断线程是否暂停,如果是则等待件满足;否则,增加进度值,并发射进度值变化的信号。
- msleep(self, milliseconds):线程休眠的方法,以毫秒为单位。
MainWindow类
MainWindow 类是一个继承自 QWidget 的窗口类。它包含了一些状态变量和方法来管理线程和界面的交互。
属性
- thread_running:一个布尔值,标记线程是否正在运行
方法
- setup_ui:设置用户界面。创建一个垂直布局,并在布局中添加了一个进度条 QProgressBar 和四个按钮 QPushButton。这些按钮分别是 “启动”、“停止”、“恢复” 和 “结束”。
- setup_thread:设置线程。实例化一个 MyThread 对象,并将进度值变化的信号 valueChange 与进度条的 setValue 方法连接起来。在线程运行时,进度条的值会随着信号的发出而更新。同时将 thread_running 标志设置为 True,表示线程正在运行。
- start_thread:启动线程。如果 thread_running 为 True,表示线程已经存在,直接调用 start 方法来启动线程。如果 thread_running 为 False,则调用 setup_thread 方法来创建并设置线程,然后再启动线程。
- paused_thread:用于暂停线程。如果线程没有运行,即 isRunning() 返回 False,则调用 start 方法来启动线程。否则,调用线程的 pause_thread 方法来暂停线程的执行。
- resume_thread:用于恢复线程的执行。如果线程没有运行,即 thread_running 为 False,则直接返回。否则,调用线程的 resume_thread 方法来恢复线程的执行。
- stop_thread:用于停止线程。它调用线程的 quit 方法来终止线程的事件循环,并将 thread_running 标志设置为 False,表示线程已停止。同时,将进度条的值重置为 0
运行结果
总结🎈🎈
本文中,展示了使用 PySide6/PyQT实现多线程编程,实现了一个具有启动、暂停、恢复和终止功能的线程。
本文虽然是一个简单的示例,但它也将PySide6/PyQT多线程开发中该用到的知识点都用上了,算是抛砖引玉吧。希望本文能够帮助读者理解和应用 PySide6 的多线程功能。
后话
本次分享到此结束,
see you~🐱🏍🐱🏍