QProgressBar导致QT5性能不佳?

时间:2016-04-23 12:40:13

标签: performance qt python-3.x pyqt5 qprogressbar

我正在开发一个解析文件(365000行)的程序,在这个程序中我尝试在读取每一行后匹配一些关键字。此计算以及if (!Array.from) { Array.from = (function () { var toStr = Object.prototype.toString; var isCallable = function (fn) { return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; }; var toInteger = function (value) { var number = Number(value); if (isNaN(number)) { return 0; } if (number === 0 || !isFinite(number)) { return number; } return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); }; var maxSafeInteger = Math.pow(2, 53) - 1; var toLength = function (value) { var len = toInteger(value); return Math.min(Math.max(len, 0), maxSafeInteger); }; // The length property of the from method is 1. return function from(arrayLike/*, mapFn, thisArg */) { // 1. Let C be the this value. var C = this; // 2. Let items be ToObject(arrayLike). var items = Object(arrayLike); // 3. ReturnIfAbrupt(items). if (arrayLike == null) { throw new TypeError("Array.from requires an array-like object - not null or undefined"); } // 4. If mapfn is undefined, then let mapping be false. var mapFn = arguments.length > 1 ? arguments[1] : void undefined; var T; if (typeof mapFn !== 'undefined') { // 5. else // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. if (!isCallable(mapFn)) { throw new TypeError('Array.from: when provided, the second argument must be a function'); } // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. if (arguments.length > 2) { T = arguments[2]; } } // 10. Let lenValue be Get(items, "length"). // 11. Let len be ToLength(lenValue). var len = toLength(items.length); // 13. If IsConstructor(C) is true, then // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len. // 14. a. Else, Let A be ArrayCreate(len). var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Let k be 0. var k = 0; // 17. Repeat, while k < len… (also steps a - h) var kValue; while (k < len) { kValue = items[k]; if (mapFn) { A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); } else { A[k] = kValue; } k += 1; } // 18. Let putStatus be Put(A, "length", len, true). A.length = len; // 20. Return A. return A; }; }()); } 的更新是在另一个使用QProgressBar的线程中进行的。一切正常,除了性能,特别是当我更新QThread时。我使用计时器进行解析,结果只是STUNNING。当我发出信号来更新QProgressBar时,程序大约需要45秒,但是当我没有发出QProgressBar更新的信号时,程序大约需要0.40秒= /

QProgressBar

控制台输出:

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()

我错过了什么或是预期的吗?

编辑:

我遵循jpo38和Matteo非常好的建议。我不太频繁地更新QProgressBar。进展仍然顺利,性能非常好(使用此实现大约一秒钟)。 PSB:

over : 44.49321101765038  //QProgressBar updated
over : 0.3695987798147516 //QProgressBar not updated

2 个答案:

答案 0 :(得分:3)

过于频繁地更新QProgressBar肯定会导致性能问题。您应该不经常更新进度条。您不希望/需要为每次迭代执行此操作... 365000次。当您从365000中读取一行时,您的进度为0.0002%,无需为此更新GUI ...

向用户显示进度总是有成本...我们接受这一点,因为用户更喜欢等待一些并获得进度信息。但是,显示进度不能将处理时间乘以100。

只有当进度发生显着变化时(例如,每次转换为int的百分比值发生变化时,您都可以发出信号来更新进度条,您可以将进度存储为int值来检查...或者测试(line%(fileLines/100)==0)例如......这将显着降低进度条更新的成本。)

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

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

答案 1 :(得分:2)

这是一个经典的问题,我认识的每一位经验丰富的开发人员都有一个关于一个长期过程的故事,其中大部分时间实际上都是由进度条更新完成的(大部分故事最终完全取消了进度条)

重点是,通常,工作单位&#34;你处理(在你的情况下解析一行)比进度条更新的成本要小 - GUI与用户反应比较快,但与解析单行相比仍然相当重量级(特别是如果涉及跨线程机械)。

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

  • 如果您注意到您的流程一般是&#34;快速&#34;你只需要删除进度条(或者用那些无用的&#34;向前和向后替换#34;进度条只是为了表明如果程序有时被提供的文件比平时更大,你的程序就不会被挂起) ;
  • 你只是不经常更新它;你可以每总进度的1/100发出信号;进步仍然是顺利的,你不应该有性能问题(100次更新不会花费很多时间,虽然我猜他们仍然会占据你的过程所花费的时间,如果它通常需要0.40秒) ;
  • 您可以完全解除进度条更新与实际执行操作的代码。而不是发出信号,用当前进度更新整数类成员(应该非常便宜);在GUI线程中使用一个计时器根据这个成员每隔一个 - 比如0.5秒来更新进度条。如果过程在第一个计时器滴答之前完成,您甚至可以更聪明并避免完全显示进度条。