pyQt学习笔记——QThread线程


在 PyQt 中,主线程负责 GUI 的事件循环,因此如果执行耗时任务(如视频处理、深度学习推理等)可能会导致界面卡顿甚至无响应。为了避免这个问题,我们通常使用 QThread 创建后台线程来处理这些任务。本教程将详细介绍 QThread 的使用,包括线程的创建、启动、通信以及正确的管理方式。

为什么要使用 QThread?

在 PyQt 中,默认所有操作都在 主线程(GUI 线程) 中执行。如果执行耗时任务(如读取摄像头视频、文件处理、深度学习推理等),就会阻塞 GUI,使界面卡顿甚至无响应。

解决方案:

  • 使用 QThread 创建后台线程,避免阻塞 GUI。
  • 通过信号和槽(Signal & Slot)进行线程间通信,确保数据传输安全。

创建 QThread 线程

在 PyQt 中,我们可以通过两种方式创建线程:

  1. 继承 QThread 并重写 run() 方法(推荐使用)
  2. 使用 moveToThread() 方式(适用于复杂对象)

这里主要介绍第一种方式。

继承 QThread 创建线程

from PyQt5.QtCore import QThread, pyqtSignal
import time

class WorkerThread(QThread):
    update_signal = pyqtSignal(str)  # 定义信号,向主线程发送字符串

    def __init__(self):
        super().__init__()
        self.is_running = True  # 控制线程是否运行

    def run(self):
        """线程执行的任务"""
        while self.is_running:
            time.sleep(1)  # 模拟耗时任务
            self.update_signal.emit("线程运行中...")  # 发送信号
        self.update_signal.emit("线程已停止")

    def stop(self):
        """停止线程"""
        self.is_running = False

在 GUI 界面中使用 QThread

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel
from PyQt5.QtCore import Qt
import sys

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QThread 多线程示例")
        self.resize(300, 200)

        self.layout = QVBoxLayout()
        self.label = QLabel("点击按钮启动线程")
        self.button_start = QPushButton("启动线程")
        self.button_stop = QPushButton("停止线程")

        self.layout.addWidget(self.label)
        self.layout.addWidget(self.button_start)
        self.layout.addWidget(self.button_stop)
        self.setLayout(self.layout)

        # 创建线程
        self.thread = WorkerThread()
        self.thread.update_signal.connect(self.update_label)  # 连接信号
        
        # 绑定按钮事件
        self.button_start.clicked.connect(self.start_thread)
        self.button_stop.clicked.connect(self.stop_thread)

    def start_thread(self):
        if not self.thread.isRunning():
            self.thread.is_running = True
            self.thread.start()

    def stop_thread(self):
        self.thread.stop()

    def update_label(self, message):
        self.label.setText(message)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

线程间通信(Signal & Slot)

在 PyQt 中,QThread 不能直接更新 UI,需要通过 信号(Signal)和槽(Slot) 进行通信。

  • pyqtSignal:定义一个信号
  • connect():连接信号和槽
  • emit():在线程中发送信号

示例:

class WorkerThread(QThread):
    update_signal = pyqtSignal(str)

    def run(self):
        for i in range(5):
            time.sleep(1)
            self.update_signal.emit(f"线程运行中:{i}")  # 发送信号

线程安全的停止方法

在 PyQt5 中,不能直接使用 terminate() 结束线程,正确的方法是 使用标志位 控制 run() 方法。

class WorkerThread(QThread):
    def __init__(self):
        super().__init__()
        self.is_running = True

    def run(self):
        while self.is_running:
            time.sleep(1)
            print("线程运行中")
    
    def stop(self):
        self.is_running = False

多线程处理摄像头视频流

import cv2
import os
from PyQt5.QtCore import QThread, pyqtSignal

class CameraThread(QThread):
    frame_signal = pyqtSignal(object)  # 发送视频帧

    def __init__(self):
        super().__init__()
        self.cap = cv2.VideoCapture(0)  # 打开摄像头
        self.is_running = True  # 控制线程是否运行
    
    def run(self):
        while self.is_running:
            ret, frame = self.cap.read()
            if ret:
                self.frame_signal.emit(frame)  # 发送视频帧到 UI 进行显示
    
    def stop(self):
        self.is_running = False  # 停止线程循环
        self.cap.release()  # 释放摄像头资源

结束线程释放资源

if self.thread.isRunning():
    self.thread.stop()
    self.thread.quit()
    self.thread.wait()

总结

  • 使用 QThread 处理耗时任务,防止 GUI 卡死。
  • 通过 pyqtSignal 进行线程间通信,避免直接操作 UI。
  • 使用 is_running 变量安全停止线程,避免 terminate() 可能引发的问题。

适用场景:

  • 处理摄像头视频流(如 YOLO 目标检测)
  • 数据处理、文件下载等耗时任务
  • 后台计算任务,如深度学习推理
在Python中,可以使用QThread类来创建线程,并通过调用start()方法来启动线程。要挂起或暂停QThread线程,可以使用QThread类的sleep()方法来实现。该方法会使线程进入睡眠状态,暂停一段时间后再继续执行。例如,可以在线程的run()方法中使用sleep()方法来实现线程的暂停。以下是一个示例代码: ```python from PyQt5.QtCore import QThread, QTimer class MyThread(QThread): def __init__(self): super().__init__() def run(self): # 执行一些耗时操作 self.do_something() def do_something(self): # 模拟耗时操作 for i in range(10): print(i) self.sleep(1) # 暂停1秒钟 # 创建线程实例 thread = MyThread() # 启动线程 thread.start() ``` 在上面的示例中,线程会执行一个耗时操作do_something(),在每次循环中暂停1秒钟。通过调用sleep()方法,线程会在每次循环之间暂停一段时间,实现线程的挂起效果。需要注意的是,sleep()方法是线程QThread的方法,而不是Python的threading模块的方法。因此,在使用QThread类时,应该使用QThread的sleep()方法来实现线程的挂起。 #### 引用[.reference_title] - *1* [Python如何安全地挂起、恢复、终止Qthread线程](https://blog.youkuaiyun.com/cqwjw1989/article/details/115905356)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Python多任务—线程](https://blog.youkuaiyun.com/weixin_39860952/article/details/110766971)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [【Python_PySide2学习笔记(九)】基于QThread实现 子线程更新 主线程/UI线程](https://blog.youkuaiyun.com/sallyyellow/article/details/128610873)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tt555555555555

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值