python pyside2 QThread线程

介绍

在 PySide2 中,线程处理是避免 UI 卡顿的关键技术,尤其是在执行耗时操作时。以下是 PySide2 线程使用的详细指南和示例:

应用场景

例子

import sys
import time
from PySide2.QtWidgets import (QApplication, QWidget, QVBoxLayout, 
                               QPushButton, QTextEdit, QProgressBar)
from PySide2.QtCore import QThread, Signal, Qt

# 1. 自定义线程类(继承QThread)
class WorkerThread(QThread):
    # 定义信号:用于向主线程发送数据
    update_signal = Signal(str)       # 发送文本更新
    progress_signal = Signal(int)     # 发送进度更新
    finished_signal = Signal()        # 发送任务完成信号

    def __init__(self, task_duration=10):
        super().__init__()
        self.task_duration = task_duration  # 任务持续时间(秒)
        self.is_running = True              # 控制线程运行的标志

    def run(self):
        """线程执行的任务(耗时操作放在这里)"""
        try:
            for i in range(101):
                # 检查是否需要停止
                if not self.is_running:
                    self.update_signal.emit("任务被取消")
                    return

                # 发送进度更新
                self.progress_signal.emit(i)

                # 发送文本更新
                if i % 10 == 0 and i > 0:
                    self.update_signal.emit(f"任务进度:{i}%")

                # 模拟耗时操作
                time.sleep(self.task_duration / 100)

            # 任务完成
            self.update_signal.emit("任务完成!")
            self.finished_signal.emit()

        except Exception as e:
            self.update_signal.emit(f"发生错误:{str(e)}")

    def stop(self):
        """停止线程"""
        self.is_running = False
        self.wait()  # 等待线程结束


# 2. 主窗口类
class ThreadDemoWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.thread = None  # 线程实例
        self.initUI()

    def initUI(self):
        # 创建布局
        layout = QVBoxLayout()
        layout.setSpacing(15)

        # 文本显示区域
        self.text_edit = QTextEdit()
        self.text_edit.setReadOnly(True)
        layout.addWidget(self.text_edit)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        layout.addWidget(self.progress_bar)

        # 控制按钮
        self.start_btn = QPushButton("开始任务")
        self.start_btn.clicked.connect(self.start_thread)

        self.stop_btn = QPushButton("停止任务")
        self.stop_btn.clicked.connect(self.stop_thread)
        self.stop_btn.setEnabled(False)  # 初始禁用

        # 添加按钮到布局
        btn_layout = QVBoxLayout()
        btn_layout.addWidget(self.start_btn)
        btn_layout.addWidget(self.stop_btn)
        layout.addLayout(btn_layout)

        self.setLayout(layout)

        # 窗口设置
        self.setWindowTitle("PySide2 线程示例")
        self.setGeometry(300, 300, 500, 300)
        self.show()

    def start_thread(self):
        """启动线程"""
        # 确保之前的线程已停止
        if self.thread and self.thread.isRunning():
            self.thread.stop()

        # 创建并配置线程
        self.thread = WorkerThread(task_duration=5)  # 任务持续5秒

        # 连接信号与槽
        self.thread.update_signal.connect(self.update_text)
        self.thread.progress_signal.connect(self.update_progress)
        self.thread.finished_signal.connect(self.on_task_finished)

        # 启动线程
        self.thread.start()

        # 更新UI状态
        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.text_edit.append("任务开始...")

    def stop_thread(self):
        """停止线程"""
        if self.thread and self.thread.isRunning():
            self.thread.stop()
            self.update_text("任务已停止")
            self.on_task_finished()

    def update_text(self, message):
        """更新文本显示(在主线程中执行)"""
        self.text_edit.append(message)

    def update_progress(self, value):
        """更新进度条(在主线程中执行)"""
        self.progress_bar.setValue(value)

    def on_task_finished(self):
        """任务完成后更新UI状态"""
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)

    def closeEvent(self, event):
        """窗口关闭时确保线程已停止"""
        if self.thread and self.thread.isRunning():
            self.thread.stop()
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ThreadDemoWindow()
    sys.exit(app.exec_())

线程使用核心要点

  1. 线程创建方式

    • 继承 QThread 并重写 run() 方法(示例中使用的方式)
    • 使用 QRunnable 和 QThreadPool(适合短期任务)
  2. 线程与UI通信

    • 必须通过 信号(Signal) 和 槽(Slot) 进行通信
    • 绝对不要在子线程中直接操作UI控件(会导致崩溃)
  3. 线程生命周期管理

    • 使用标志位(如 is_running)控制线程安全退出
    • 线程结束时调用 wait() 确保资源正确释放
    • 窗口关闭前停止所有线程
  4. 线程安全注意事项

    • 共享数据需要加锁(使用 QMutex)
    • 避免在子线程中创建或销毁UI对象
    • 长时间运行的线程应提供停止机制

常见应用场景

  • 文件IO操作(读取大文件、批量处理文件)
  • 网络请求(API调用、数据下载)
  • 耗时计算(数据分析、图像处理)
  • 定时任务(周期性数据更新)

使用线程可以确保UI在执行耗时操作时保持响应,提升用户体验。示例中展示的线程通信模式是PySide2中最常用和推荐的方式。


原文出处:/show_1GW1rlplwTIE.html