PyQt5界面无响应的解决方案

 更新时间:2024年05月09日 15:02:48   作者:Lorin洛林  
如果在主线程执行耗时操作,比如 循环、sleep、wait 异步线程执行 会导致 UI 界面进入无响应状态,我们可以采用以下两种方式异步处理:使用QThread 或 QTimer,本文给大家介绍了PyQt5界面无响应的解决方案,需要的朋友可以参考下

前言

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

到此这篇关于PyQt5界面无响应的解决方案的文章就介绍到这了,更多相关PyQt5界面无响应内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python检测远程udp端口是否打开的方法

    python检测远程udp端口是否打开的方法

    这篇文章主要介绍了python检测远程udp端口是否打开的方法,涉及Python操作socket实现检测udp端口的技巧,需要的朋友可以参考下
    2015-03-03
  • python实现象棋游戏

    python实现象棋游戏

    这篇文章主要为大家详细介绍了python实现象棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • opencv python 图像轮廓/检测轮廓/绘制轮廓的方法

    opencv python 图像轮廓/检测轮廓/绘制轮廓的方法

    这篇文章主要介绍了opencv python 图像轮廓/检测轮廓/绘制轮廓的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • tensorflow之读取jpg图像长和宽实例

    tensorflow之读取jpg图像长和宽实例

    这篇文章主要介绍了tensorflow之读取jpg图像长和宽实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • PyQt5实现暗黑风格的计时器

    PyQt5实现暗黑风格的计时器

    这篇文章主要为大家详细介绍了PyQt5实现暗黑风格的计时器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Python中用post、get方式提交数据的方法示例

    Python中用post、get方式提交数据的方法示例

    最近在学习使用Python,发现网上很少提到如何使用post,所以下面这篇文章主要给大家介绍了关于Python中用post、get方式提交数据的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-09-09
  • Python浪漫玫瑰盛开表白源代码

    Python浪漫玫瑰盛开表白源代码

    要知道我们程序猿也是需要浪漫的,小编我之前在网上搜寻了很多代码,却发现好多都不是最新的,所以自己就整理了一下代码,现在与广大博友们分享下
    2021-09-09
  • Python中的lstrip()方法使用简介

    Python中的lstrip()方法使用简介

    这篇文章主要介绍了Python中的lstrip()方法使用简介,是Python入门的基础知识,需要的朋友可以参考下
    2015-05-05
  • Python argparse模块应用实例解析

    Python argparse模块应用实例解析

    这篇文章主要介绍了Python argparse模块应用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • python通过urllib2爬网页上种子下载示例

    python通过urllib2爬网页上种子下载示例

    这篇文章主要介绍了通过urllib2、re模块抓种子下载的示例,需要的朋友可以参考下
    2014-02-02

最新评论