PyQt5 解决界面无响应方案

前言

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

版本

  • PyQt5
  • Python 3.x

案例

  • 我们写一个简单的进度条填充程序,每 2 秒填充 1%:
python 复制代码
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 异步线程执行:
python 复制代码
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 定时器异步执行:
python 复制代码
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 未响应状态:
python 复制代码
    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编程。

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

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

相关推荐
郭庆汝4 小时前
pytorch、torchvision与python版本对应关系
人工智能·pytorch·python
思则变7 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
漫谈网络8 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket
try2find9 小时前
安装llama-cpp-python踩坑记
开发语言·python·llama
博观而约取10 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector11 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习12 小时前
Python入门Day2
开发语言·python
Vertira12 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉12 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗12 小时前
黑马python(二十四)
开发语言·python