Python PyQt5——QThread使用方法与代码实践

QThread

在 GUI 程序中,如果想要让程序执行一项耗时的工作,例如下载文件、I/O 存取等,深度学习模型推理,直接在 UI 线程中进行这些操作会导致整个界面卡住,出现无响应的状态。为了避免这种情况,可以将这些耗时任务放在另一个线程中执行。在 PyQt 中,可以使用 QThread 来实现这一点。

基本用法

在 PyQt5 中,要使用 QThread 创建一个线程,需要创建 QThread 的子类,并重写 QThread.run() 函数。以下是一个示例,WorkerThread 类继承自 QThread 并重写了 run() 方法:

class WorkerThread(QThread):def __init__(self):super().__init__()def run(self):# 执行耗时任务

接下来,可以使用 QThread.start() 来启动线程。在构造 WorkerThread 后,不会立即执行 run() 方法,直到调用 QThread.start() 才开始执行。如果主线程需要等待线程执行完毕,可以使用 QThread.wait(),它会等待线程完成才返回。但请注意,如果线程中包含无限循环,QThread.wait() 将会无限期等待。

下面是一个简单的示例:

import sys
import time
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QThreadclass WorkerThread(QThread):def __init__(self):super().__init__()def run(self):for i in range(3):time.sleep(1)print('WorkerThread::run ' + str(i))if __name__ == '__main__':app = QApplication(sys.argv)print('main')work1 = WorkerThread()work2 = WorkerThread()work1.start()work2.start()work1.wait()work2.wait()print('end of main')sys.exit(app.exec_())

在这个 run 函数中,有一个循环执行 3 次,并在每次循环后休眠 1 秒,然后输出一条消息。从输出结果可以看出,主线程会等待两个 WorkerThread 线程都完成后才输出 “end of main” 消息,这证明了使用 QThread.wait() 是有效的。

错误堵塞机制

在 PyQt5 开发过程中,新手容易错误地使用线程,导致界面卡住或变黑。例如,一个下载文件的程序,按下按钮后会执行大约 10 秒的工作。如果在 UI 线程中直接执行这些操作,会发现整个 GUI 程序无法进行其他操作,界面会卡住或变黑。

正确的做法是在 WorkerThread 中定义两个信号,分别为 trigger 和 finished。finished 信号在工作完成后发送,而 trigger 信号在执行过程中发送。使用 pyqtSignal 来定义自定义信号,并指定发送到目标函数的参数类型。例如,trigger = pyqtSignal(str)。

整个程序在按下按钮后,会开启另一个线程,每秒更新一次秒数到标签上,通过 self.trigger.emit(str(i+1)) 发射信号并传递第几秒的参数。第 5 秒时结束线程,并通过 self.finished.emit() 发射结束信号。

下面是一个示例代码:

import sys
import time
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton
from PyQt5.QtCore import QThread, pyqtSignalclass WorkerThread(QThread):trigger = pyqtSignal(str)finished = pyqtSignal()def __init__(self):super().__init__()def run(self):for i in range(5):time.sleep(1)self.trigger.emit(str(i+1))print('WorkerThread::run ' + str(i))self.finished.emit()class MyWidget(QWidget):def __init__(self):super().__init__()self.initUI()def initUI(self):self.setWindowTitle('my window')self.setGeometry(50, 50, 200, 150)layout = QVBoxLayout()self.setLayout(layout)self.mylabel = QLabel('press button to start thread', self)layout.addWidget(self.mylabel)self.mybutton = QPushButton('start', self)self.mybutton.clicked.connect(self.startThread)layout.addWidget(self.mybutton)self.work = WorkerThread()def startThread(self):self.mybutton.setDisabled(True)self.work.start()self.work.trigger.connect(self.updateLabel)self.work.finished.connect(self.threadFinished)self.updateLabel(str(0))def threadFinished(self):self.mybutton.setDisabled(False)def updateLabel(self, text):self.mylabel.setText(text)if __name__ == '__main__':app = QApplication(sys.argv)w = MyWidget()w.show()sys.exit(app.exec_())

结果如下:

main
press button to start thread
1
2
3
4
end of main

如果不想在 threadFinished 函数中只是执行一段代码,可以省略 threadFinished 函数,改用 lambda 表达式。例如,将 self.mybutton.setDisabled(False) 操作写在 self.work.finished.connect() 中的 lambda 表达式里。

在 QWidget 里使用 QThread

在 PyQt 程序中,主线程就是所说的 UI 线程,UI 线程会处理所有控件的事务。因此,如果有耗时的工作需要执行,通常不会将其放在 UI 线程中,因为这样做会阻止其他控件的更新,导致界面卡顿或程序无响应。解决这个问题的方法是创建另一个线程来处理这些耗时的工作。

在 WorkerThread 中新增了两个信号,分别命名为 trigger 和 finished。finished 信号在工作完成后发送,而 trigger 信号在执行过程中发送。当需要自定义信号时,使用 pyqtSignal 来定义要发送到目标函数的函数原型,例如在下面的示例中,trigger = pyqtSignal(str) 表示 trigger 信号会携带一个字符串参数。

整个程序的工作流程是:当按下按钮后,会启动另一个线程,这个线程每一秒更新一次秒数到标签上。通过 self.trigger.emit(str(i+1)) 发送 trigger 信号,并传递当前的秒数作为参数。当到达第 5 秒时,线程结束,并通过 self.finished.emit() 发送 finished 信号。

这种设计允许我们在不影响 UI 响应性的情况下执行长时间的任务,同时还能更新 UI 显示当前的进度或状态。通过使用信号和槽机制,可以在工作线程和 UI 线程之间安全地传递信息,确保程序的流畅运行。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import time
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout,QLabel, QPushButton)
from PyQt5.QtCore import QThread, pyqtSignalclass WorkerThread(QThread):trigger = pyqtSignal(str)finished = pyqtSignal()def __init__(self):super().__init__()def run(self):for i in range(5):time.sleep(1)self.trigger.emit(str(i+1))print('WorkerThread::run ' + str(i))self.finished.emit()class MyWidget(QWidget):def __init__(self):super().__init__()self.initUI()def initUI(self):self.setWindowTitle('my window')self.setGeometry(50, 50, 200, 150)layout = QVBoxLayout()self.setLayout(layout)self.mylabel = QLabel('press button to start thread', self)layout.addWidget(self.mylabel)self.mybutton = QPushButton('start', self)self.mybutton.clicked.connect(self.startThread)layout.addWidget(self.mybutton)self.work = WorkerThread()def startThread(self):self.mybutton.setDisabled(True)self.work.start()self.work.trigger.connect(self.updateLabel)self.work.finished.connect(self.threadFinished)self.updateLabel(str(0))def threadFinished(self):self.mybutton.setDisabled(False)def updateLabel(self, text):self.mylabel.setText(text)if __name__ == '__main__':app = QApplication(sys.argv)w = MyWidget()w.show()sys.exit(app.exec_())

如果你不希望 threadFinished 函数仅仅是执行一行代码,而是想去掉 threadFinished 函数,可以将 self.mybutton.setDisabled(False) 操作写在 self.work.finished.connect() 里的 lambda 表达式中。以下是使用 lambda 表达式的示例:

def startThread(self):self.mybutton.setDisabled(True)self.work.start()self.work.trigger.connect(self.updateLabel)# self.work.finished.connect(self.threadFinished)self.work.finished.connect(lambda: self.mybutton.setDisabled(False))self.updateLabel(str(0))# def threadFinished(self):
#    self.mybutton.setDisabled(False)def updateLabel(self, text):self.mylabel.setText(text)

这样,当 finished 信号被触发时,lambda 表达式中的代码将会执行,即启用按钮。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/789804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux提权!!!

上一篇文章讲了Windows的提权,那么这篇文章就来讲一下Linux的提权 1.SUID提权 suid权限 作用:让普通用户临时拥有该文件的属主的执行权限,suid权限只能应用在二进制可执行文件(命令)上,而且suid权限只能设置…

HR是怎么看待PMP证书的呢?

考PMP证书为什么值得?对管理人员有用么? 首先,在行业内部,一名项目经理,需要有PMP证书已经是行业内的共识了。而且面试的时候,如果是同样的年龄段,同样的背景,那有证书在手的人&…

瑞吉外卖实战学习--13、完善删除中的逻辑

完善删除中的逻辑 前言效果图逻辑介绍表结构根据mybatisPlus接口规范创建实体类和service和mapper文件1、实体类Dish和Setmeal2、Mapper接口DishMapper和SetealMapper3、Service接口DishService和setmealService4、Service实现类DishServiceImpl和setmealServicelmpl 编写删除函…

【御控物联】JSON结构数据转换在物流调度系统中的应用(场景案例三)

文章目录 一、前言二、场景概述三、解决方案四、在线转换工具五、技术资料 一、前言 物流调度是每个生产厂区必不可少的一个环节,主要包括线边物流和智能仓储。线边物流是指将物料定时、定点、定量配送到生产作业一线的环节,其包括从集中仓库到线边仓、…

【详解】Windows系统安装Nginx及简单使用

【详解】Windows系统安装Nginx及简单使用 一、Nginx是什么? “Nginx 是一款轻量级的 HTTP 服务器,采用事件驱动的异步非阻塞处理方式框架,这让其具有极好的 IO 性能,时常用于服务端的反向代理和负载均衡。”Nginx 是一款 http 服…

鸿蒙OS开发实例:【组件化模式】

组件化一直是移动端比较流行的开发方式,有着编译运行快,业务逻辑分明,任务划分清晰等优点,针对Android端的组件化;与Android端的组件化相比,HarmonyOS的组件化可以说实现起来就颇费一番周折,因为…

IoT数采平台4:测试

IoT数采平台1:开篇IoT数采平台2:文档IoT数采平台3:功能IoT数采平台4:测试 Modbus RTU串口测试 OPC测试 HTTP测试 MQTT透传测试 MQTT网关测试及数据上报 TCP / UDP 监听,客户端连上后发送信息,客户端上报数据…

pygame--坦克大战(一)

项目搭建 本游戏主要分为两个对象,分别是我方坦克和敌方坦克。用户可以通过控制我方的坦克来摧毁敌方的坦克保护自己的“家”,把所有的敌方坦克消灭完达到胜利。敌方的坦克在初始的时候是默认5个的(这可以自己设置),当然,如果我方坦克被敌方坦克的子弹打中,游戏结束。从…

Lambda表达式,Stream流

文章目录 Lambda表达式作用前提函数式接口特点 语法省略模式和匿名对象类的区别 Stream流思想作用三类方法获取方法单列集合(Collection[List,Set双列集合Map(不能直接获取)数组同一类型元素(Stream中的静态方法) 常见的中间方法终结方法收集方法 Optional类 Lambda表达式 作用…

每日三个JAVA经典面试题(三十四)

1.Mybatis的一级、二级缓存 MyBatis提供了两种缓存机制来提高查询效率:一级缓存和二级缓存。 一级缓存(Session级别) 作用范围:一级缓存是基于SqlSession的。这意味着,如果你在同一个SqlSession中执行两次相同的查询…

数据结构(六)——图的存储及基本操作

6.2 图的存储及基本操作 6.2.1 邻接矩阵法 邻接矩阵存储无向图、有向图 #define MaxVertexNum 100 //顶点数目的最大值typedef struct{char Vex[MaxVertexNum]; //顶点表int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表int vexnum,arcnum; //图的当前…

[C++]std::map用法

map用法详解 Map是STL的一个关联容器,它提供一对一的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一 种非严格意义上…

c模板编程c/c++20240401

c模板编程 #include<iostream> //#include<string> //#include<algorithm> template <typename T> T max(T a, T b) { return (a > b) ? a : b; } int main() { int i max(1, 2); // 返回 2 float f max(3.14f, 2.72f); // 返回 3…

华为ICT七力助推文化产业新质生产力发展

创新起主导作用的新质生产力由新劳动者、新劳动对象、新劳动工具、新基础设施等四大要素共同构成&#xff0c;符合新发展理念的先进生产力质态&#xff1b;具有高科技、高能效、高质量等三大突出特征。而通过壮大新产业、打造新模式、激发新动能&#xff0c;新质生产力能够摆脱…

阿里云数据库服务器价格表,2024年最新报价

阿里云数据库服务器价格表&#xff0c;优惠99元一年起&#xff0c;ECS云服务器2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;优惠价格99元一年&#xff1b;阿里云数据库MySQL版2核2G基础系列经济版99元1年、2核4GB 227.99元1年&#xff0c;云数据库PostgreSQL、SQL Serve…

使用LangChain连接MySQL并实现Chain多伦问答,基于智谱API

学习目标&#xff1a; 掌握LangChain基础用法 环境&#xff1a; JupyterLabPython 3.12.2 导入基础包 !pip install langchain -i https://mirrors.aliyun.com/pypi/simple/ !pip install zhipuai -i https://mirrors.aliyun.com/pypi/simple/ !pip install langchain_open…

探索 ZKFair 的Dargon Slayer蓝图,解锁新阶段的潜力

在当前区块链技术的发展中&#xff0c;Layer 2&#xff08;L2&#xff09;解决方案已成为提高区块链扩容性、降低交易成本和提升交易速度的关键技术&#xff0c;但它仍面临一些关键问题和挑战&#xff0c;例如用户体验的改进、跨链互操作性、安全性以及去中心化程度。在这些背景…

西圣、飞利浦、万魔开放式耳机好用吗?热门产品硬核测评对比

在无线耳机市场中&#xff0c;开放式耳机因其独特的佩戴方式和出色的听音体验逐渐受到消费者的青睐&#xff0c;西圣、飞利浦、万魔作为业内知名品牌&#xff0c;都推出了各自的开放式耳机产品&#xff0c;备受关注&#xff0c;那么这些热门品牌的开放式耳机究竟好用吗&#xf…

【JVM】如何定位、解决内存泄漏和溢出

目录 1.概述 2.堆溢出、内存泄定位及解决办法 2.1.示例代码 2.2.抓堆快照 2.3.分析堆快照 1.概述 常见的几种JVM内存溢出的场景如下&#xff1a; Java堆溢出&#xff1a; 错误信息: java.lang.OutOfMemoryError: Java heap space 原因&#xff1a;Java对象实例在运行时持…

Open3D(C++) 基于随机抽样与特征值法的点云平面稳健拟合方法

目录 一、算法原理1、论文概述2、参考文献二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的GPT爬虫。 一、算法原理 1、论文概述 针对点云数据含有异常值且传统拟合方法拟合结果不理想的情…