Performance QProgressBar在QT5中导致性能不良?

Performance QProgressBar在QT5中导致性能不良?,performance,qt,python-3.x,pyqt5,qprogressbar,Performance,Qt,Python 3.x,Pyqt5,Qprogressbar,我正在开发一个程序,它解析一个文件(365000行),在读取每一行之后,我尝试在其中匹配一些关键字。在另一个线程中使用QThread进行此计算以及我的QProgressBar的更新。除了性能之外,一切都很好,尤其是当我更新QProgressBar时。我使用一个计时器进行解析,结果令人震惊。当我发出更新QProgressBar的信号时,程序大约需要45秒,但当我没有发出更新QProgressBar的信号时,程序大约需要0.40秒=/ from PyQt5 import QtCore, QtWidg

我正在开发一个程序,它解析一个文件(365000行),在读取每一行之后,我尝试在其中匹配一些关键字。在另一个线程中使用
QThread
进行此计算以及我的
QProgressBar
的更新。除了性能之外,一切都很好,尤其是当我更新
QProgressBar
时。我使用一个计时器进行解析,结果令人震惊。当我发出更新
QProgressBar
的信号时,程序大约需要45秒,但当我没有发出更新
QProgressBar
的信号时,程序大约需要0.40秒=/

from PyQt5 import QtCore, QtWidgets, QtGui
import sys
import time

liste = ["failed", "exception"]

class ParseFileAsync(QtCore.QThread):

    match = QtCore.pyqtSignal(str)
    PBupdate = QtCore.pyqtSignal(int)
    PBMax = QtCore.pyqtSignal(int)

    def run(self):        
        cpt = 0
        with open("test.txt", "r") as fichier:
            fileLines = fichier.readlines()  
            lineNumber = len(fileLines)    
            self.PBMax.emit(lineNumber)

            t0 = time.time()
            for line in fileLines:
                cpt+=1
                self.PBupdate.emit(cpt)   
                for element in liste:
                    if element in line:
                        self.match.emit(line)

        finalTime = time.time() - t0
        print("over :", finalTime)

    class Ui_MainWindow(QtWidgets.QMainWindow):

        def __init__(self):
            super().__init__()       
            self.setupUi(self)
            self.thread = ParseFileAsync()

            self.thread.match.connect(self.printError)
            self.thread.PBupdate.connect(self.updateProgressBar)
            self.thread.PBMax.connect(self.setMaximumProgressBar)

            self.pushButton_GO.clicked.connect(self.startThread)

    def printError(self, line):
        self.textEdit.append(line)

    def updateProgressBar(self, value):
        self.progressBar.setValue(value)

    def setMaximumProgressBar(self, value):
        self.progressBar.setMaximum(value)

    def startThread(self):
        self.thread.start()
控制台输出:

over : 44.49321101765038  //QProgressBar updated
over : 0.3695987798147516 //QProgressBar not updated
我是错过了什么,还是期待着

编辑:

我听从了jpo38和Matteo的建议。我更新QProgressBar的频率较低。进展仍然顺利,性能非常好(使用此实现大约1秒)。公共广播局:

类ParseFileAsync(QtCore.QThread): match=QtCore.pyqtSignal(str) PBupdate=QtCore.pyqtSignal(int) PBMax=QtCore.pyqtSignal(int) def运行(自): 以open(“test_long.log”、“r”)作为fichier: fileLines=fichier.readlines() self.lineNumber=len(文件行) self.PBMax.emit(self.lineNumber) 如果(自身行号<30): self.parseFile(文件行,False) 其他: self.parseFile(文件行,True) def parseFile(self、fileLines、isBig): cpt=0 如果(是大的): 对于文件行中的行: cpt+=1 如果(cpt%(int(self.lineNumber/30))==0: self.PBupdate.emit(cpt) 对于列表中的元素: 如果元素在行中: self.match.emit(行) self.PBupdate.emit(self.lineNumber)#避免QProgressBar在99%时停止 其他: 对于文件行中的行: cpt+=1 self.PBupdate.emit(cpt) 对于列表中的元素: 如果元素在行中: self.match.emit(行)
过于频繁地更新
QProgressBar
肯定会导致性能问题。您应该减少更新进度条的频率。您不希望/不需要每次迭代都这样做……365000次。当您从365000行中读取一行时,您的进度为0.0002%,无需为此更新GUI

向用户显示进度总是要付出代价的……我们接受这一点,因为用户更愿意等待一段时间并获得进度信息。但是,显示进度不能像您所经历的那样将处理时间乘以100

您可以仅在进度发生显著变化时发出更新进度条的信号(例如,每次转换为
int
的百分比值发生变化时,您可以将进度存储为
int
值以检查…或测试是否
(line%(fileLines/100)==0)
例如……这将大大降低进度条更新的成本)

或者你可以启动一个
QTimer
,例如每100毫秒更新一次进度条。然后,您不会从
for
循环发出任何信号,只需保存进度值,以便在计时器超时时使用


如果文件大小始终为365000行,您也可以决定每1000行发出一次信号,例如(
如果行%1000==0
)。但是,前面的两种解决方案更可取,因为无论文件大小如何,它们都会解决性能问题。

过于频繁地更新
QProgressBar肯定会导致性能问题。您应该减少更新进度条的频率。您不希望/不需要每次迭代都这样做……365000次。当您从365000行中读取一行时,您的进度为0.0002%,无需为此更新GUI

向用户显示进度总是要付出代价的……我们接受这一点,因为用户更愿意等待一段时间并获得进度信息。但是,显示进度不能像您所经历的那样将处理时间乘以100

您可以仅在进度发生显著变化时发出更新进度条的信号(例如,每次转换为
int
的百分比值发生变化时,您可以将进度存储为
int
值以检查…或测试是否
(line%(fileLines/100)==0)
例如……这将大大降低进度条更新的成本)

或者你可以启动一个
QTimer
,例如每100毫秒更新一次进度条。然后,您不会从
for
循环发出任何信号,只需保存进度值,以便在计时器超时时使用


如果文件大小始终为365000行,您也可以决定每1000行发出一次信号,例如(
如果行%1000==0
)。但是前面的两种解决方案更可取,因为无论文件大小如何,它们都能解决性能问题。

这是一个典型的问题,我认识的每一位有经验的开发人员都有一个故事,讲述了一个假定很长的过程,其中大部分时间实际上是由progressbar更新占用的(这些故事中的大多数以完全删除进度条而告终)

关键是,通常情况下,您处理的“工作单元”(在您的例子中是对一行的解析)比进度条更新的成本小得多-GUI与用户反应相比速度快,但与解析一行(例如,涉及跨线程机制的情况下)相比,仍然相当重

根据我的经验,有三种常见的解决方案:

  • 如果您注意到您的进程通常是“快速”的,您只需删除进度条(或者用那些没有帮助的“向前和向后”进度条替换它,以表明如果
    class ParseFileAsync(QtCore.QThread):
    
    match = QtCore.pyqtSignal(str)
    PBupdate = QtCore.pyqtSignal(int)
    PBMax = QtCore.pyqtSignal(int)
    
    def run(self):        
        with open("test_long.log", "r") as fichier:
            fileLines = fichier.readlines()  
            self.lineNumber = len(fileLines)
            self.PBMax.emit(self.lineNumber)
    
            if (self.lineNumber < 30):
                self.parseFile(fileLines, False)
            else:
                self.parseFile(fileLines, True)
    
    def parseFile(self, fileLines, isBig):                
            cpt = 0
    
            if(isBig):
                for line in fileLines:
                    cpt+=1             
                    if(cpt % (int(self.lineNumber/30)) == 0):
                        self.PBupdate.emit(cpt)        
                    for element in liste:
                        if element in line:
                            self.match.emit(line)
    
                self.PBupdate.emit(self.lineNumber) #To avoid QProgressBar stopping at 99%
            else:         
                for line in fileLines:
                    cpt+=1                
                    self.PBupdate.emit(cpt)                                  
                    for element in liste:
                        if element in line:
                            self.match.emit(line)