31. 多线程技术

一、多线程技术

  在 Pyside6 中,主线程一般负责界面绘制和更新。当执行某些复杂且耗时的操作时,如果将执行这些操作的代码放在主线程中,界面就会出现停止响应(或卡顿)的情况。

  QThread 类是 PySide6 中的核心线程类,要实现一个线程,需要创建 QThread 类的有一个子类,并且实现其 run() 方法。

  线程也有自己的生命周期,其中包含 5 种状态,分别为:新建状态就绪状态运行状态阻塞状态死亡状态新建状态 就是线程被创建时的状态;当线程对象调用 start() 方法后,线程就处于 就绪状态,当线程得到系统资源后就进入 运行状态

  一旦线程进入运行状态,它会在就绪和运行状态下切换,同时也可能进入阻塞或死亡状态。当处于 运行状态 下的线程在调用 sleep()wait() 方法或者发生阻塞时,会进入 暂停状态;当在休眠结束或者阻塞解除时,线程会重新进入 就绪状态;当线程的 run() 方法执行完毕,或者线程发生错误、异常时,线程就进入 死亡状态

  我们可以在终端中使用 pip 安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载

pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple

  国内常用的 pip 下载源列表:

  QThread 类的常用方法及其说明如下:

start(priority:QThread.Priority=QThread.Priority.InheritPriority) -> None       # 启动线程
run() -> None                                                                   # 线程的起点

wait(time:int) -> None                                                          # 阻塞线程

usleep(us:int) -> None                                                          # 以微秒为单位休眠线程
msleep(ms:int) -> None                                                          # 以毫秒为单位休眠线程
sleep(s:int) -> None                                                            # 以秒为单位休眠线程

exit(retcode:int=0) -> int                                                      # 退出线程的事件循环,并返回代码,返回0表示成功,任何非0值都表示错误
quit() -> int                                                                   # 退出线程的事件循环,并返回代码0(成功),相当于exit(0)

terminate() -> None                                                             # 强制终止线程

setPriority(priority:QThread.Priority.IdlePriority) -> None                     # 设置线程的优先级

isRunning() -> bool                                                             # 获取线程是否在运行
isFinished() -> bool                                                            # 获取线程是否完成

  QThread 类常用的信号及其说明如下:

started()                                                                       # 在调用run()方法之前,在相关线程开始执行时从该线程发射
finished()                                                                      # 在相关线程完成执行之前,从该线程发射

  我们可以调用 setPriority(priority:QThread.Priority) 方法来 设置线程的优先级,其中参数 priorityQThread.Priority 类型的枚举值,可以取值如下:

QThread.Priority.IdlePriority                                                   # 空闲优先级
QThread.Priority.LowestPriority                                                 # 最低优先级
QThread.Priority.LowPriority                                                    # 低优先级
QThread.Priority.NormalPriority                                                 # 系统默认优先级
QThread.Priority.HighPriority                                                   # 高优先级
QThread.Priority.HighestPriority                                                # 最高优先级
QThread.Priority.TimeCriticalPriority                                           # 尽可能频繁地分配执行
QThread.Priority.InheritPriority                                                # 默认用与创建线程相同地优先级

  在调用 start() 方法 启动线程 后,新创建的线程将调用 run() 方法开始执行。如果我们向强制终止线程,可以调用 terminate() 方法,在 terminate() 方法之后应该使用 wait() 方法,以确保当线程终止时,等待完成的所有线程都将被唤醒。

  新建一个 thread.py 文件,用来存放线程相关的代码。

from PySide6.QtCore import QThread, Signal

class MyThread(QThread):
    number_signal = Signal(int)                                                 # 创建信号

    def __init__(self):
        super(MyThread, self).__init__()                                        # 调用父类的初始化方法
        self.flag = True
        self.number = 0

    def run(self):
        """重写run()方法"""
        self.flag = True
        self.number = 0

        while True:
            if self.flag:
                self.number += 1
                self.number_signal.emit(self.number)                            # 发送信号
                self.msleep(100)                                                # 线程休眠100毫秒

  新建一个 ui.py 文件,用来存放 UI 相关的代码。

from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QLabel, QPushButton
from PySide6.QtWidgets import QVBoxLayout
from PySide6.QtCore import Qt

class MyUi:
    def setupUi(self, window:QWidget):
        window.resize(100, 100)                                                 # 1.设置窗口对象大小

        layout = QVBoxLayout(window)                                            # 2.创建一个垂直布局

        self.label = QLabel()                                                   # 3.创建一个标签,并添加到垂直布局中
        layout.addWidget(self.label)

        self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)                   # 4.设置标签的文本居中
        self.label.setText("0")                                                 # 5.设置标签的文本

        self.start_button = QPushButton("开始")                                  # 6.创建一个按钮,并添加到垂直布局中
        layout.addWidget(self.start_button)

        self.stop_button = QPushButton("停止")
        layout.addWidget(self.stop_button)

  新建一个 widget.py 文件,用来存放业务逻辑相关的代码。

import sys

from PySide6.QtWidgets import QApplication, QWidget

from ui import MyUi
from thread import MyThread

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()                                                      # 1.调用父类Qwidget类的__init__()方法

        self.__ui = MyUi()
        self.__ui.setupUi(self)                                                 # 2.初始化页面

        self.count_thread = MyThread()                                          # 3.创建线程对象

        self.__ui.start_button.clicked.connect(self.start_button_clicked)
        self.__ui.stop_button.clicked.connect(self.stop_button_clicked)

        self.count_thread.number_signal.connect(self.update_label)

    def start_button_clicked(self):
        if self.__ui.start_button.text() == "开始":
            if not self.count_thread.isRunning():                               # 1.如果线程没有运行
                self.count_thread.start()                                       # 2.启动线程
            else:                                                               # 3.如果线程正在运行
                self.count_thread.flag = True                                   # 4.设置线程的运行标志为True
            self.__ui.start_button.setText("暂停")
        elif self.__ui.start_button.text() == "暂停":
            self.count_thread.flag = False                                      # 5.设置线程的运行标志为False   
            self.__ui.start_button.setText("开始")
        elif self.__ui.start_button.text() == "重启":
            if not self.count_thread.isRunning():
                self.count_thread.start()
                self.__ui.start_button.setText("暂停")
      
    def stop_button_clicked(self):
        self.count_thread.terminate()
        self.count_thread.wait()
        self.__ui.start_button.setText("重启")

    def update_label(self, value:int):
        self.__ui.label.setText(str(value))

if __name__ == "__main__":
    app = QApplication(sys.argv)                                                # 1.创建一个QApplication类的实例
    window = MyWidget()                                                         # 2.创建一个窗口
    window.show()                                                               # 3.展示窗口
    sys.exit(app.exec())                                                        # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
posted @ 2025-01-19 17:28  星光映梦  阅读(126)  评论(0)    收藏  举报