PyQt多线程使用

最近在做一个系统。对代码进行主线程和子线程的分离,将比较耗时的数据处理操作放在子线程里面操作,防止卡死。由于最后系统使用的环境很杂,pyqt5版本过高,在一些xp的低版本操作系统是运行会出问题,所以使用的pyqt4,但原理一样。
原理参考博文最后链接,这里记录自己的使用方法

比如,在通过实现制作TFrecords文件,并记录文件照片数量。这里有循环读取的耗时操作。利用多线程实现如下:

步骤:

1.创建线程类

from PyQt4 import QtCore
from PyQt4.QtCore import pyqtSignal


class WorkThread(QtCore.QThread):#定义一个工作线程,后面会调用和重写
    # 使用信号和UI主线程通讯,参数是发送信号时附带参数的数据类型,可以是str、int、list等
    finishSignal = pyqtSignal(int)

    def run(self):#线程启动后会自动执行,这里是逻辑实现的代码
        self.finishSignal.emit(int) #发射信号

WorkThread是创建的一个线程类,后面会重写他的run方法
实现制作数据集的操作:

class dealThread(WorkThread): #1.制作数据集
    finishSignal = pyqtSignal(int,int)

    # 带3个参数
    def __init__(self,dataset_param,total_label,tf_record):
        super(dealThread, self).__init__()

        self.datasetparam_ui = dataset_param
        self.totalnum_label = total_label
        self.Tfrecord_ui = tf_record

    #处理逻辑业务
    def run(self):
        try:
            if self.datasetparam_ui.tfrecordstart_radioButton.isChecked():# Tfrecord文件
                saveTfrecord_path = self.datasetparam_ui.datasetsavepath_label.text()  # 保存路径
                ftrecordfilename = ("train.tfrecords")  # tfrecords格式文件名

                # 通过这一句将数据写入到TFRecord文件,参数为TFrecord的文件路径,入口
                writer = tf.io.TFRecordWriter(saveTfrecord_path + "\\" + ftrecordfilename)

                # 标签
                with open(self.datasetparam_ui.csvlabel_label.text(), 'r') as ff:
                    reader = csv.reader(ff)
                    images_list = []
                    labels = []
                    for row in reader:
                        images_list.append(row[0])
                        labels.append(row[1])
                ff.close()

                lines = 1
                bad = 0
          
<think> 我们正在讨论PyQt6中的多线程使用。根据引用内容,PyQt(包括PyQt6)中多线程的正确使用方式主要有两种:子类化QThread和moveToThread方法。下面我们将详细介绍这两种方法,并提供示例代码。 ### 1. 子类化QThread 这是比较传统的方法,通过继承QThread并重写run方法来实现多线程。 **步骤:** 1. 创建一个继承自`QThread`的类 2. 重写`run()`方法,将耗时代码放在这里 3. 使用信号(signal)与主线程通信 **示例代码:** ```python from PyQt6.QtCore import QThread, pyqtSignal class WorkerThread(QThread): # 定义一个信号,用于在任务完成时发送数据,int为信号传递的数据类型 finished_signal = pyqtSignal(int) def __init__(self, parent=None): super().__init__(parent) def run(self): # 这里是耗时的操作,比如数据处理、网络请求等 result = 0 for i in range(1, 1001): result += i # 可以发送进度信号,例如:progress_signal.emit(i) self.finished_signal.emit(result) # 任务完成时发送结果 ``` **使用方式:** ```python # 在主窗口中创建线程 self.worker = WorkerThread() self.worker.finished_signal.connect(self.on_worker_finished) self.worker.start() # 启动线程 # 处理完成后的槽函数 def on_worker_finished(self, result): print("计算结果:", result) ``` ### 2. 使用moveToThread方法 这种方法将工作对象移动到新线程中,通过信号触发工作对象的槽函数。这种方法更符合Qt的推荐方式,因为它避免了继承,更灵活。 **步骤:** 1. 创建一个工作类(通常继承自`QObject`),其中包含需要在线程中执行的方法(槽函数) 2. 创建`QThread`和这个工作类的实例 3. 将工作对象移动到新线程 4. 通过信号触发工作对象的槽函数 5. 注意:工作对象中的槽函数不能有阻塞主线程的操作(如死循环),如果需要,应使用线程安全的信号来更新UI **示例代码:** ```python from PyQt6.QtCore import QObject, QThread, pyqtSignal class Worker(QObject): finished = pyqtSignal(int) def do_work(self): result = 0 for i in range(1, 1001): result += i self.finished.emit(result) # 在主窗口中使用 self.worker = Worker() self.thread = QThread() self.worker.moveToThread(self.thread) # 连接信号 self.worker.finished.connect(self.on_worker_finished) # 当线程启动时,调用worker的do_work方法 self.thread.started.connect(self.worker.do_work) # 启动线程 self.thread.start() ``` **停止线程:** 当需要停止线程时,应该安全地退出: ```python # 停止线程 self.worker.finished.connect(self.thread.quit) # 任务完成后退出线程 self.worker.finished.connect(self.worker.deleteLater) # 删除worker对象 self.thread.finished.connect(self.thread.deleteLater) # 删除线程对象 ``` ### 注意事项 1. **线程安全**:在子线程中不能直接操作UI元素,必须通过信号与主线程通信来更新UI。 2. **资源释放**:线程结束后,应正确释放资源,避免内存泄漏。 3. **避免阻塞**:在子线程中执行耗时操作,确保不会阻塞主线程(UI线程)。 4. **线程管理**:如果有多个任务,可以使用线程池(`QThreadPool`)来管理。 ### 线程池的使用 对于大量的小任务,使用线程池可以提高效率: ```python from PyQt6.QtCore import QRunnable, QThreadPool, pyqtSlot, QObject, pyqtSignal class WorkerSignals(QObject): result = pyqtSignal(int) class Task(QRunnable): def __init__(self, n): super().__init__() self.n = n self.signals = WorkerSignals() @pyqtSlot() def run(self): # 计算任务 result = sum(range(self.n)) self.signals.result.emit(result) # 使用线程池 thread_pool = QThreadPool() task = Task(1000) task.signals.result.connect(self.on_result) thread_pool.start(task) ``` ### 示例:在PyQt6应用中使用多线程 下面是一个简单的窗口应用,点击按钮启动一个耗时计算任务,并在任务完成时更新UI。 ```python import sys from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget from PyQt6.QtCore import QThread, pyqtSignal class WorkerThread(QThread): finished_signal = pyqtSignal(int) def run(self): result = 0 for i in range(1, 1000001): # 累加到一百万 result += i self.finished_signal.emit(result) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("多线程示例") self.setGeometry(100, 100, 300, 200) layout = QVBoxLayout() self.btn_start = QPushButton("开始计算") self.btn_start.clicked.connect(self.start_task) layout.addWidget(self.btn_start) self.label_result = QLabel("结果将显示在这里") layout.addWidget(self.label_result) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def start_task(self): self.btn_start.setEnabled(False) self.label_result.setText("计算中...") self.worker = WorkerThread() self.worker.finished_signal.connect(self.on_task_finished) self.worker.start() def on_task_finished(self, result): self.label_result.setText(f"计算结果: {result}") self.btn_start.setEnabled(True) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) ``` ### 总结 在PyQt6中使用多线程,推荐两种方式: 1. **子类化`QThread`**:适合简单的单一任务线程。 2. **`moveToThread`**:适合需要多个任务方法的情况,更灵活。 无论哪种方式,都要注意: - 不在子线程中操作UI - 使用信号进行线程间通信 - 妥善管理线程的生命周期 使用多线程可以有效避免UI卡顿,提升用户体验[^1][^2][^4]。 §§相关问题§§ 1. PyQt6中如何停止一个正在运行的线程? 2. PyQt6线程间通信有哪些方式? 3. 在多线程中如何避免资源竞争? 4. PyQt6中的线程池(QThreadPool)如何使用? 5. 如何实现PyQt6多线程的进度条更新? [^1]: Qt官方文档: QThread Class [^2]: PyQt6多线程编程指南 [^4]: 多线程编程最佳实践 </think> ### PyQt6 多线程实现方法详解 在 PyQt6 中,多线程处理主要使用 `QThread` 类来实现。以下是两种推荐的使用方法,基于引用内容的最佳实践: --- #### 方法一:子类化 QThread(继承方式) 这是最直接的方法,适合单一任务线程的场景: ```python from PyQt6.QtCore import QThread, pyqtSignal class WorkerThread(QThread): # 定义信号用于线程通信(类型根据实际需要指定) progress_signal = pyqtSignal(int) result_signal = pyqtSignal(object) def run(self): """线程执行的核心方法""" try: result = 0 # 模拟耗时操作 for i in range(1, 101): time.sleep(0.1) result += i self.progress_signal.emit(i) # 发送进度信号 self.result_signal.emit(result) # 发送结果信号 except Exception as e: self.result_signal.emit(str(e)) ``` **使用方式:** ```python # 创建线程实例 worker = WorkerThread() # 连接信号与槽 worker.progress_signal.connect(self.update_progress_bar) worker.result_signal.connect(self.handle_result) # 启动线程 worker.start() ``` **特点:** - `run()` 方法是线程入口点 - 通过信号机制与主线程通信 - 自动回收资源(线程结束后自动删除) - 适合单一任务的场景[^1] --- #### 方法二:使用 moveToThread(对象转移方式) 更灵活的方法,适合需要在同一线程中执行多个任务的场景: ```python from PyQt6.QtCore import QObject, QThread, pyqtSignal class Worker(QObject): finished = pyqtSignal(object) def process(self): """耗时操作""" result = [] for i in range(1000): result.append(i ** 2) self.finished.emit(result) # 在主窗口中设置 class MainWindow(QMainWindow): def __init__(self): super().__init__() # 创建工作对象和线程 self.worker = Worker() self.thread = QThread() # 将worker移动到新线程 self.worker.moveToThread(self.thread) # 连接信号 self.thread.started.connect(self.worker.process) self.worker.finished.connect(self.on_finished) self.worker.finished.connect(self.thread.quit) # 启动线程 self.thread.start() def on_finished(self, result): print("结果:", len(result)) ``` **特点:** - 将现有对象移动到新线程 - 支持在同一线程执行多个方法 - 线程生命周期管理更灵活 - 适合需要复用线程的场景[^4][^5] --- ### 关键注意事项 1. **线程安全原则:** - **禁止**在子线程中直接操作 GUI 元素 - 所有 UI 更新必须通过信号/槽机制进行 - 共享资源需使用线程锁(`QMutex`) 2. **内存管理:** ```python # 线程结束时自动清理 worker.finished.connect(worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) ``` 3. **错误处理:** ```python # 连接线程的错误信号 worker.error_signal.connect(self.handle_error) ``` 4. **进度更新示例:** ```python # 在主线程更新进度条 def update_progress(self, value): self.progressBar.setValue(value) # 注意:此方法必须在主线程调用 ``` --- ### 完整示例:带进度条的多线程任务 ```python from PyQt6.QtWidgets import QApplication, QMainWindow, QProgressBar, QPushButton from PyQt6.QtCore import QThread, pyqtSignal import sys, time class WorkerThread(QThread): progress = pyqtSignal(int) result = pyqtSignal(int) def run(self): total = 0 for i in range(1, 101): time.sleep(0.05) # 模拟耗时操作 total += i self.progress.emit(i) self.result.emit(total) class MainWindow(QMainWindow): def __init__(self): super().__init__() # 创建UI self.progress = QProgressBar() self.button = QPushButton("开始计算") self.button.clicked.connect(self.start_task) # 布局代码省略... def start_task(self): self.button.setEnabled(False) self.worker = WorkerThread() self.worker.progress.connect(self.progress.setValue) self.worker.result.connect(self.task_finished) self.worker.start() def task_finished(self, result): print(f"计算结果: {result}") self.button.setEnabled(True) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() app.exec() ``` --- ### 方法对比 | 方法 | 适用场景 | 通信方式 | 资源管理 | |-------------------|----------------------------------|--------------|---------------| | 子类化 QThread | 单一任务线程 | 自定义信号 | 自动回收 | | moveToThread | 同一线程多个任务 | 对象信号 | 需手动管理 | > **提示**:PyQt6 多线程的核心原则是**保持UI响应**和**线程安全**。对于简单任务推荐方法一,复杂场景推荐方法二[^2][^4]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值