Bootstrap

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 写法,会被当做当前作用域中的局部变量,当前作用域代码执行完成后就会被销毁,不再继续执行。

这里给大家分享一份Python全套学习资料,包括学习路线、软件、源码、视频、面试题等等,都是我自己学习时整理的,希望可以对正在学习或者想要学习Python的朋友有帮助!

CSDN大礼包:全网最全《全套Python学习资料》免费分享🎁

😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

👉CSDN大礼包🎁:全网最全《Python学习资料》免费分享(安全链接,放心点击)👈

1️⃣零基础入门

① 学习路线

对于从来没有接触过Python的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

② 路线对应学习视频

还有很多适合0基础入门的学习视频,有了这些视频,轻轻松松上手Python~在这里插入图片描述

③练习题

每节视频课后,都有对应的练习题哦,可以检验学习成果哈哈!
在这里插入图片描述
因篇幅有限,仅展示部分资料

2️⃣国内外Python书籍、文档

① 文档和书籍资料

在这里插入图片描述

3️⃣Python工具包+项目源码合集

①Python工具包

学习Python常用的开发软件都在这里了!每个都有详细的安装教程,保证你可以安装成功哦!
在这里插入图片描述

②Python实战案例

光学理论是没用的,要学会跟着一起敲代码,动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。100+实战案例源码等你来拿!
在这里插入图片描述

③Python小游戏源码

如果觉得上面的实战案例有点枯燥,可以试试自己用Python编写小游戏,让你的学习过程中增添一点趣味!
在这里插入图片描述

4️⃣Python面试题

我们学会了Python之后,有了技能就可以出去找工作啦!下面这些面试题是都来自阿里、腾讯、字节等一线互联网大厂,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
在这里插入图片描述
在这里插入图片描述

5️⃣Python兼职渠道

而且学会Python以后,还可以在各大兼职平台接单赚钱,各种兼职渠道+兼职注意事项+如何和客户沟通,我都整理成文档了。
在这里插入图片描述
在这里插入图片描述
上述所有资料 ⚡️ ,朋友们如果有需要 📦《全套Python学习资料》的,可以扫描下方二维码免费领取 🆓
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

👉CSDN大礼包🎁:全网最全《Python学习资料》免费分享(安全链接,放心点击)👈

;