2016-10-12 150 views
1

我在OSX上使用Python 3.5,PyQt5,我想知道是否有可能在不減慢整個計算工作的情況下更新QProgressBar。 這是我的代碼,如果我只是沒有進度條更新的任務,它太快了!Python3,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_()) 

然後在從stackoverflow用戶的幫助下,我得到了一個提示,使單獨的工作線程和更新信號連接到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_()) 
+0

如何在沒有進度條的情況下調用該函數?我認爲它不是嵌套循環? – UnholySheep

+0

@UnholySheep只需註釋掉'#update progressbar'下面的3行即可。 – saitam

+0

所以顯而易見的問題是:你如何衡量性能/運行時間?你爲什麼在UI線程中執行繁重的工作(而不是單獨的線程)? – UnholySheep

回答

0

有三件事情的代碼拖慢:

  1. 打印到標準輸出是非常昂貴的 - 尤其是當你做50萬次!在我的系統上,註釋掉print(i,j)大概一半時間doAction需要運行。
  2. processEvents稱爲500,000次也相當昂貴。註釋掉QApplication.processEvents()可將運行時間縮短三分之二。
  3. 註釋掉self.pbar.setValue(step)再次減半。

希望現在應該很明顯,嘗試更新貴500,000次應該需要少於一秒的任務是巨大的矯枉過正!大多數用戶的反應時間最多約爲200ms,因此您只需要每100ms更新一次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') 
+0

感謝您的信息和評論'print()'ist好,但我需要更新並顯示和更新'pbar'爲我的應用程序。否則我不會問這個問題:-) – saitam

+0

對不起,我沒有閱讀完整的答案。外循環是有道理的,謝謝:-)但我必須弄清楚如何用線程來完成。 – saitam

+0

@saitam。我已經添加了一些線程示例代碼。 – ekhumoro