2016-04-23 45 views
0

我正在開發一個解析文件(365000行)的程序,其中我嘗試在讀取每行後匹配一些關鍵字。此計算與我的QProgressBar的更新一起在另一個線程中使用QThread進行。一切工作正常,除了性能,尤其是當我更新QProgressBar。我使用解析器的計時器,結果就是STUNNING。當我發出的信號來更新QProgressBar程序佔用約45秒,但我不發射信號的QProgressBar更新則程序約需0.40秒=/QProgressBar在QT5中導致性能不佳?

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和利瑪竇非常好的建議。我不太經常更新QProgressBar。進展仍然順利,表現非常好(這個實施大約一秒鐘)。 PSB:

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) 
+0

您使用的是哪個版本的Qt?也許你剛剛被https://bugreports.qt.io/browse/QTBUG-49655 – peppe

+0

Thx咬了你的回覆。有趣的地方......它似乎確實是同一個問題。我正在使用V5.5.1。 – Trunks10

+0

你可以試試5.6,看看它是否解決了這個問題? – peppe

回答

3

更新一個QProgressBar太頻繁肯定會導致性能問題。您應該不經常更新進度欄。你不需要/需要爲每次迭代... 365000次。當您從365000中讀取一行時,您的進度增加了0.0002%,無需爲此更新GUI ...

向用戶顯示進度始終有成本......並且我們接受這種情況,因爲用戶更喜歡等待多一點,並有進度信息。但是,顯示進展不能像您所經歷的那樣將處理時間乘以100。

您可以發出只更新進度條的信號時,進展顯著變化(例如澆鑄爲int每次百分比值發生變化,您可以將進程存儲爲int值檢查...或者測試,如果例如(line%(fileLines/100)==0) ...這將顯着降低進度欄更新的成本)。

或者您可以開始QTimer以每100ms更新一次進度條。然後,您不會從for循環發出任何信號,只保存定時器超時時使用的進程值。

如果文件大小始終爲365000行,則也可以決定每隔1000行發送一次信號(if line%1000==0)。但是前兩種解決方案是可取的,因爲無論文件大小如何,它們都可以解決性能問題。

+0

非常好的解釋謝謝:)。我編輯了我的第一篇文章。 – Trunks10

+0

@ Trunks10。好。您可能更喜歡將一個帶有isBig參數的parseFile函數分解爲兩個函數的代碼。 – jpo38

+0

好主意thx! – Trunks10

2

這是一個典型的問題,我知道每個有經驗的開發人員都有一個關於一個長時間過程的故事,其中大部分時間實際上是由進度條更新取得的(大多數故事最終會完全移除進度條)。

問題是,很多時候,您處理的「工作單元」(在您的情況下解析一條線)比進度條更新的成本要小 - 圖形用戶界面與用戶反射,但與解析單行數據(尤其涉及跨線程機制)相比,仍然相當重量級。

根據我的經驗,有三種常見的解決方案:

  • 如果您發現您的一般過程是「快」,你剛落進度(或那些無益的「向前和向後」進步更換隻是爲了表明你的程序沒有被掛起,如果程序有時被文件比平時更大的文件);
  • 你只是更新頻率較低;你可以每完成一次你的信號發射你的信號;進展依然順利,你不應該有性能問題(100次更新不會花費太多時間,但我猜他們仍然會支配你的進程花費的時間,如果它通常需要0.40秒);
  • 你可以從代碼中完全去除進度條更新,而代碼實際上完成了這些工作。用當前進度更新一個整數類成員(應該很便宜),而不是發出信號;在GUI線程中使用一個計時器來根據這個成員更新進度條 - 比如說0.5秒。如果過程在第一個計時器打勾之前完成,您甚至可以變得更聰明並避免完全顯示進度條。
+0

非常好的解釋,謝謝;)。我編輯了我的第一篇文章,並選擇不經常更新QProgressBar。 – Trunks10