PyQt5 解决界面无响应方案

news2025/1/11 9:50:52

文章目录

  • 前言
  • 版本
  • 案例
  • 解决方案
    • `QThread`
    • `QTimer`
  • 局部变量创建异步线程导致 UI 未响应
    • 如果 `QTimer 不使用 self.time` 写法
  • 个人简介

前言

  • 在PyQt5中,GUI线程通常指的是Qt的主事件循环线程,也称为主线程。主线程负责处理GUI事件、更新UI界面等任务。在PyQt5中,主线程和GUI线程是同一个线程,即运行应用程序的线程。
  • 当创建一个Qt应用程序时,主线程会启动,并执行QApplication.exec_()方法,进入Qt的事件循环。在事件循环中,主线程会不断地监听并处理用户的输入事件、定时器事件、网络事件等,然后更新UI界面。
  • 如果在主线程执行耗时操作,比如 循环、sleep、wait 异步线程执行 会导致 UI 界面进入无响应状态,我们可以采用以下两种方式异步处理:使用QThread 或 QTimer

版本

  • PyQt5
  • Python 3.x

案例

  • 我们写一个简单的进度条填充程序,每 2 秒填充 1%:
import sys
import time

from PyQt5.QtWidgets import QApplication, QWidget, QProgressBar, QPushButton, QHBoxLayout


class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.currentValue = 0

        self.progressBar = QProgressBar(self)
        self.progressBar.resize(200, 50)
        self.progressBar.move(20, 20)
        self.progressBar.setValue(self.currentValue)

        # 创建一个按钮
        self.button = QPushButton('点击我', self)
        self.button.clicked.connect(self.on_clicked)

        # 创建一个垂直布局,并将按钮添加到布局中
        layout = QHBoxLayout()
        layout.addWidget(self.progressBar)
        layout.addWidget(self.button)

        # 设置窗口的主布局为垂直布局
        self.setLayout(layout)

    def on_clicked(self):
        while True:
            time.sleep(2)
            self.currentValue = (self.currentValue + 1) % 101
            self.progressBar.setValue(self.currentValue)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.resize(500, 300)
    w.move(300, 300)
    w.setWindowTitle('Simple')
    w.show()
    sys.exit(app.exec_())
  • 点击运行,我们会发现 UI 界面出现无响应且进度条没有刷新:

解决方案

  • 为了避免 UI 界面无响应,我们可以采用以下两种方式:使用 QThread 或 QTimer

QThread

  • 我们可以通过点击事件创建 QThread 异步线程执行:
import sys
import time

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QProgressBar, QPushButton, QHBoxLayout


class MyWorker(QThread):
    timeout = pyqtSignal()

    def __init__(self):
        super(MyWorker, self).__init__()

    def run(self):
        while True:
            time.sleep(2)
            self.timeout.emit()


class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.worker = None
        self.currentValue = 0

        self.progressBar = QProgressBar(self)
        self.progressBar.resize(200, 50)
        self.progressBar.move(20, 20)
        self.progressBar.setValue(self.currentValue)

        # 创建一个按钮
        self.button = QPushButton('点击我', self)
        self.button.clicked.connect(self.on_clicked)

        # 创建一个垂直布局,并将按钮添加到布局中
        layout = QHBoxLayout()
        layout.addWidget(self.progressBar)
        layout.addWidget(self.button)

        # 设置窗口的主布局为垂直布局
        self.setLayout(layout)

    def on_clicked(self):
        self.worker = MyWorker()
        self.worker.timeout.connect(self.upgradeProgress)
        self.worker.start()

    def upgradeProgress(self):
        self.currentValue = (self.currentValue + 1) % 101
        self.progressBar.setValue(self.currentValue)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.resize(500, 300)
    w.move(300, 300)
    w.setWindowTitle('Simple')
    w.show()
    sys.exit(app.exec_())

运行效果:

QTimer

  • 我们可以通过点击事件创建 QTimer 定时器异步执行:
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QProgressBar, QPushButton, QHBoxLayout


class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.currentValue = 0

        self.progressBar = QProgressBar(self)
        self.progressBar.resize(200, 50)
        self.progressBar.move(20, 20)
        self.progressBar.setValue(self.currentValue)

        # 创建一个按钮
        self.button = QPushButton('点击我', self)
        self.button.clicked.connect(self.on_clicked)

        # 创建一个垂直布局,并将按钮添加到布局中
        layout = QHBoxLayout()
        layout.addWidget(self.progressBar)
        layout.addWidget(self.button)

        # 设置窗口的主布局为垂直布局
        self.setLayout(layout)

    def on_clicked(self):
        # 定义一个定时器并启动定时器
        self.time = QTimer()
        self.time.timeout.connect(self.upgradeProgress)
        self.time.start(200)

    def upgradeProgress(self):
        self.currentValue = (self.currentValue + 1) % 101
        self.progressBar.setValue(self.currentValue)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWidget()
    w.resize(500, 300)
    w.move(300, 300)
    w.setWindowTitle('Simple')
    w.show()
    sys.exit(app.exec_())
  • 运行效果:

局部变量创建异步线程导致 UI 未响应

  • 在使用 QThread 的案例中,将 on_clicked 方法改为如下写法,同样会导致 UI 未响应状态:
    def on_clicked(self):
        worker = MyWorker()
        worker.timeout.connect(self.upgradeProgress)
        worker.start()
  • 这是因为在Python中,类似于 worker = MyWorker() 这样的语句创建的对象在当前作用域中是局部变量,它的生命周期与当前作用域相关联。当当前作用域的代码执行完成后局部变量会被销毁。
  • 如果异步线程的任务还没有完成,而主线程的事件循环又需要等待任务完成才能继续执行,那么就会导致GUI线程无响应。这是因为主线程被阻塞在等待异步任务的过程中,无法处理事件。
  • 为了避免这种情况,我们应该将异步线程对象存储为实例变量(即使用 self.worker = MyWorker() ),这样可以确保异步线程对象的生命周期与主对象相同,直到异步任务完成。这样即使当前作用域的代码执行完成,异步线程仍然可以继续执行,并且主线程的事件循环也不会被阻塞。

如果 QTimer 不使用 self.time 写法

  • 同理,如果不使用 self.time 写法,会被当做当前作用域中的局部变量,当前作用域代码执行完成后就会被销毁,不再继续执行。

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

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

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

相关文章

学习CSS3动画教程:手把手教你绘制跑跑卡丁车

学习之前,请先听一段音乐:等登,等登,等登等登等登!没错,这就是我们当年玩的跑跑卡丁车的背景音乐,虽然后来有了QQ飞车,但还是更喜欢跑跑卡丁车,从最初的基础板车&#xf…

js逆向,参数加密js混淆

关键词 JS 混淆、源码乱码、参数动态加密 逆向目标 题目1:抓取所有(5页)机票的价格,并计算所有机票价格的平均值,填入答案。 目标网址:https://match.yuanrenxue.cn/match/1目标接口:https://ma…

KVM:无法检查 QEMU 二进制文件 /usr/bin/qemu-kvm: 没有那个文件或目录

报错信息: 解决方法: 将qemu的安装路径添加到系统的环境变量中,以便于系统可以正确的识别qemu的位置 在此之前可以先查找一下安装的路径: [rootlocalhost ~]# sudo find / -name qemu-system-x86_64 /usr/share/bash-completi…

大数据面试题 —— 数据仓库

目录 数据仓库是什么数据仓库和数据库的区别为什么要对数据仓库分层数仓分层,以及每一层的作用维度建模的三种模型范式建模、维度建模维度建模过程,如何确定这些维度 ***维度模型的各个维度之间是怎么聚合的聚合过程的数据倾斜怎么解决?数据质…

2024年最新【SpringBoot2】开发实用篇-测试_springboot2 test(1),2024年最新2024春招BAT面试真题详解

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化! 由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、…

AI论文速读 | 2024[IJCAI]时空解耦掩码预训练的时空预测

题目: Spatial-Temporal-Decoupled Masked Pre-training for Spatiotemporal Forecasting 作者:Haotian Gao ; Renhe Jiang(姜仁和) ; Zheng Dong ; Jinliang Deng (邓锦亮); Yuxin Ma ; Xuan Song(宋轩) …

Python软件安装使用

一、搭建 Python 环境 需要安装的环境主要是两个部分 : 运行环境: Python 开发环境: PyCharm 具体详细安装见此篇博客:Python安装教程 二、创建一个项目 a) 创建一个项目 b) 选择项目所在的位置 , 并选择使用的 Python 解释器 . 注意 , 一般情况下 , PyCh…

活动回顾 |观测云 AI Agent 探索实践

亚马逊云科技“构建全球化软件和互联网新生态——ISV 行业”论坛上,观测云产品架构师刘锐发表了题为“AI Agent 可观测性探索与实践”的主题演讲,不仅展示了观测云在人工智能领域的前沿技术,更强调了在日益复杂的系统环境中,实现有…

软件设计师笔记(一)-基础要点

本文内容来自笔者学习zst 留下的笔记,虽然有点乱,但是哥已经排版过一次,将就着看吧,查缺补漏,希望大家都能通过,记得加上免费的关注!谢谢!csdn贴图真的很废人! 目录 一、…

复杂链表的复制

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/ 代码实现: /* // Defini…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Slider的使用及说明

博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ Qt常用控件 | 输入类控件 | Slider的使用及说明 文章编号:…

使用DBeaver连接postgreSql提示缺少驱动

重新安装电脑之后用dbeaver链接数据库的时候,链接PG库一直提示缺少驱动,当选择下载驱动的时候又非常非常慢经常失败,尝试了一下更改源然后下载库驱动就非常快了,当然也包括dbeaver的自动更新。 方法:点击菜单栏【窗口…

数据防泄密

随着各行各业业务数据信息化发展,各类产品研发及设计等行业,都有关乎自身发展的核心数据,包括业务数据、代码数据、 机密文档、用户数据等敏感信息,这些信息数据有以下共性: — 属于核心机密资料,万一泄密…

C语言—操作符详解(操作符、进制转换、原码反码补码、结构体)

1.操作符分类 算术操作符&#xff1a; 、- 、 * 、 / 、%移位操作符&#xff1a;<< >> //移动的是二进制位位操作符&#xff1a;& | ^ //使用二进制位进行计算赋值操作符&#…

Linux系统运维:修改docker容器与宿主机之间的端口映射,解决端口占用问题

目录 一、问题 二、docker端口映射 &#xff08;一&#xff09;docker端口映射定义 1、相关概念&#xff1a; 2、默认情况下&#xff0c;Docker容器会有一个端口映射&#xff1a; 3、端口范围&#xff1a; &#xff08;二&#xff09;配置相关 1、指定端口映射 2、随机…

3D模型如何实现拖拽打开?---模大狮模型网

在当今数字化时代&#xff0c;3D技术的应用已经深入到各行各业&#xff0c;为用户带来了更加丰富、生动的体验。然而&#xff0c;对于一些用户来说&#xff0c;打开和查看3D模型可能会面临一些困难&#xff0c;特别是在无法拖拽打开时。本文将为您揭示解决这一问题的方法&#…

c4d云渲染怎么操作?怎么使用?一文带你了解

Cinema 4D (C4D) 不仅是众多设计师所青睐的卓越三维软件&#xff0c;其自带的高效渲染器以及对云渲染农场的支持&#xff0c;都极大地拓宽了创意和生产的边界。通过利用强大的云计算资源&#xff0c;C4D能够帮助用户轻松克服渲染速度缓慢的难题&#xff0c;从而实现更加流畅和高…

2024人文艺术、社会发展与教育国际会议(ICHASDE2024)

2024人文艺术、社会发展与教育国际会议(ICHASDE2024) 会议简介 2024年国际人文、艺术、社会发展与教育会议&#xff08;ICHASDE2024&#xff09;将在中国昆明举行。会议旨在为从事“人文、艺术、社会发展和教育”的专家、学者、工程师和技术人员提供一个平台&#xff0c;分享…

学习经验分享【36】论文投稿写作(非理工科文章)

业务进一步扩展&#xff0c;可辅导非理工科偏文科性质的论文辅导&#xff0c;有需要评职称但没有时间精力研究的或者其他相关需求的朋友可咨询了解。 人工智能技术在各领域的发展和思考&#xff0c;类似这种主题的文章。

SAP-ABAP-ALV报表

1、功能介绍 什么是ALV报表 2、开发步骤 事物码: SE38创建报表程序SE93生成TCODESE43SE91消息号报表类型:简单报表、复杂报表、报表树 报表组成:查询界面(选择屏幕)、展示界面、功能按钮 基本流程: 重点:数据放在内表里,临时表,放在内存里。 3、开发实战 按标题…