Python3, PyQt5: QProgressBar 更新使得实际任务非常慢
Python3, PyQt5: QProgressBar updating makes actual task very slow
我在 OSX 上使用 Python 3.5,PyQt5,我想知道是否有可能在不减慢整个计算工作速度的情况下更新 QProgressBar。 这是我的代码,如果我只完成任务而不更新进度条,它会快得多!
from PyQt5.QtWidgets import (QWidget, QProgressBar, QPushButton, QApplication)
from jellyfish import levenshtein_distance, jaro_winkler
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.pbar = QProgressBar(self)
self.pbar.setGeometry(30, 40, 200, 25)
self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(self.doAction)
self.setGeometry(300, 300, 280, 170)
self.show()
def doAction(self):
#setup variables
step = 0
m = 1000
n = 500
step_val = 100 / (m * n)
#make task
for i in range(m):
for j in range(n):
jaro_winkler(str(i), str(j))
#show task
print(i,j)
#update progressbar
step += step_val
self.pbar.setValue(step)
QApplication.processEvents()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
然后在 Whosebug 用户的帮助下,我得到了创建一个单独的工作线程并将更新信号连接到 GUI 的提示。我做到了,它现在看起来像下面的代码。它也可以工作并且速度更快,但我不知道如何将发出的信号连接到 GUI。有人能帮帮我吗?非常感谢!
from jellyfish import jaro_winkler
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QProgressBar, QMainWindow
import time
import numpy as np
class Main_Window(QMainWindow):
def __init__(self):
super(Main_Window,self).__init__()
self.initUI()
def initUI(self):
self.pbar = QProgressBar(self)
self.pbar.setGeometry(30, 40, 200, 25)
self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(MyThread.doAction)
self.setGeometry(300, 300, 280, 170)
self.show()
def updateProgressBar(self, val):
self.pbar.setValue.connect(val)
class MySignal(QWidget):
pbar_signal = QtCore.pyqtSignal(int)
class MyThread(QtCore.QThread):
def __init__(self):
super().__init__()
def doAction(self):
t = time.time() #for time measurement
#setup variables
step = 0
m = 1000
n = 500
pbar_val = 100 / m
signal_instance = MySignal()
#make task
for i in range(m):
for j in range(n):
jaro_winkler(str(i), str(j))
signal_instance.pbar_signal.emit(pbar_val)
#measuring task time
print(np.round_(time.time() - t, 3), 'sec elapsed')
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Main_Window()
sys.exit(app.exec_())
三件事使代码变慢:
- 打印到 stdout 非常昂贵 - 特别是当您打印 500,000 次时!在我的系统上,注释掉
print(i,j)
大约将doAction
花费的时间减半 运行. - 调用
processEvents
500,000 次也相当昂贵。注释掉QApplication.processEvents()
将 运行 时间再减少三分之二。 - 注释掉
self.pbar.setValue(step)
时间又减半。
希望现在应该很明显,在本应花费不到一秒的任务中尝试更新图形用户界面 500,000 次是大材小用!大多数用户的反应时间最多只有 200 毫秒左右,因此您只需要大约每 100 毫秒更新一次 gui。
鉴于此,一个简单的解决方法是将更新移动到外循环中:
for i in range(m):
for j in range(n):
jaro_winkler(str(i), str(j))
# show task
# print(i,j)
step += step_val
# update progressbar
self.pbar.setValue(step)
QApplication.processEvents()
但更好的解决方案是将计算移动到一个单独的工作线程中,并让它定期发出自定义信号来更新进度条:
class Main_Window(QMainWindow):
...
def initUI(self):
...
self.btn.clicked.connect(self.doAction)
self.thread = MyThread()
self.thread.pbar_signal.connect(self.pbar.setValue)
def doAction(self):
if not self.thread.isRunning():
self.thread.start()
class MyThread(QtCore.QThread):
pbar_signal = QtCore.pyqtSignal(int)
def run(self):
#for time measurement
t = time.time()
#setup variables
m = 1000
n = 500
progress = step = 100 / m
#make task
for i in range(m):
for j in range(n):
jaro_winkler(str(i), str(j))
progress += step
self.pbar_signal.emit(progress)
#measuring task time
print(np.round_(time.time() - t, 3), 'sec elapsed')