翼度科技»论坛 编程开发 python 查看内容

PyQt5界面无响应的解决方案

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
前言


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


版本


  • PyQt5
  • Python 3.x

案例


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

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


  4. class MyWidget(QWidget):
  5.     def __init__(self):
  6.         super(MyWidget, self).__init__()
  7.         self.currentValue = 0

  8.         self.progressBar = QProgressBar(self)
  9.         self.progressBar.resize(200, 50)
  10.         self.progressBar.move(20, 20)
  11.         self.progressBar.setValue(self.currentValue)

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

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

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

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


  26. if __name__ == '__main__':
  27.     app = QApplication(sys.argv)
  28.     w = MyWidget()
  29.     w.resize(500, 300)
  30.     w.move(300, 300)
  31.     w.setWindowTitle('Simple')
  32.     w.show()
  33.     sys.exit(app.exec_())
复制代码

  • 点击运行,我们会发现 UI 界面出现无响应且进度条没有刷新:


解决方案


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


QThread


  • 我们可以通过点击事件创建
    1. QThread
    复制代码
    异步线程执行:
  1. import sys
  2. import time

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


  5. class MyWorker(QThread):
  6.     timeout = pyqtSignal()

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

  9.     def run(self):
  10.         while True:
  11.             time.sleep(2)
  12.             self.timeout.emit()


  13. class MyWidget(QWidget):
  14.     def __init__(self):
  15.         super(MyWidget, self).__init__()
  16.         self.worker = None
  17.         self.currentValue = 0

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

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

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

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

  31.     def on_clicked(self):
  32.         self.worker = MyWorker()
  33.         self.worker.timeout.connect(self.upgradeProgress)
  34.         self.worker.start()

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


  38. if __name__ == '__main__':
  39.     app = QApplication(sys.argv)
  40.     w = MyWidget()
  41.     w.resize(500, 300)
  42.     w.move(300, 300)
  43.     w.setWindowTitle('Simple')
  44.     w.show()
  45.     sys.exit(app.exec_())
复制代码
运行效果:


QTimer


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


  4. class MyWidget(QWidget):
  5.     def __init__(self):
  6.         super(MyWidget, self).__init__()
  7.         self.currentValue = 0

  8.         self.progressBar = QProgressBar(self)
  9.         self.progressBar.resize(200, 50)
  10.         self.progressBar.move(20, 20)
  11.         self.progressBar.setValue(self.currentValue)

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

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

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

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

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


  29. if __name__ == '__main__':
  30.     app = QApplication(sys.argv)
  31.     w = MyWidget()
  32.     w.resize(500, 300)
  33.     w.move(300, 300)
  34.     w.setWindowTitle('Simple')
  35.     w.show()
  36.     sys.exit(app.exec_())
复制代码

  • 运行效果:


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


  • 在使用
    1. QThread
    复制代码
    的案例中,将
    1. on_clicked
    复制代码
    方法改为如下写法,同样会导致 UI 未响应状态:
  1.     def on_clicked(self):
  2.         worker = MyWorker()
  3.         worker.timeout.connect(self.upgradeProgress)
  4.         worker.start()
复制代码

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

如果 QTimer 不使用 self.time 写法


  • 同理,如果不使用
    1. self.time
    复制代码
    写法,会被当做当前作用域中的局部变量,当前作用域代码执行完成后就会被销毁,不再继续执行。
到此这篇关于PyQt5界面无响应的解决方案的文章就介绍到这了,更多相关PyQt5界面无响应内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:https://www.jb51.net/python/320841hcu.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具