介绍
在 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_())
线程使用核心要点
线程创建方式
- 继承
QThread并重写run()方法(示例中使用的方式) - 使用
QRunnable和QThreadPool(适合短期任务)
- 继承
线程与UI通信
- 必须通过 信号(Signal) 和 槽(Slot) 进行通信
- 绝对不要在子线程中直接操作UI控件(会导致崩溃)
线程生命周期管理
- 使用标志位(如
is_running)控制线程安全退出 - 线程结束时调用
wait()确保资源正确释放 - 窗口关闭前停止所有线程
- 使用标志位(如
线程安全注意事项
- 共享数据需要加锁(使用
QMutex) - 避免在子线程中创建或销毁UI对象
- 长时间运行的线程应提供停止机制
- 共享数据需要加锁(使用
常见应用场景
- 文件IO操作(读取大文件、批量处理文件)
- 网络请求(API调用、数据下载)
- 耗时计算(数据分析、图像处理)
- 定时任务(周期性数据更新)
使用线程可以确保UI在执行耗时操作时保持响应,提升用户体验。示例中展示的线程通信模式是PySide2中最常用和推荐的方式。