2016-10-12 17 views
1

Я использую Python 3.5, PyQt5 на OSX, и мне было интересно, есть ли возможность обновить QProgressBar, не замедляя всю вычислительную работу. Вот мой код, и если бы я выполнил задание без обновления progressbar, это было намного быстрее!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. Я сделал это, и теперь он выглядит следующим образом. Он также работает и работает намного быстрее, но я не могу понять, как подключить излучаемый сигнал к графическому интерфейсу. Может кто-нибудь, пожалуйста, помогите мне? Спасибо заранее!

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 Просто закомментируйте 3 строки ниже '#update progressbar' – saitam

+0

Итак, очевидные вопросы: как вы оценили производительность/время выполнения? Почему вы выполняете тяжелую работу в своем потоке пользовательского интерфейса (вместо отдельного потока)? – UnholySheep

ответ

0

Есть три вещи, которые замедляют код вниз:

  1. Печать на стандартный вывод является очень дорогим - особенно, когда вы делаете это 500.000 раз! В моей системе, комментируя print(i,j), примерно половина времени doAction берет на себя.
  2. Это также довольно дорого можно назвать processEvents 500 000 раз. Комментирование QApplication.processEvents() сокращает время выполнения еще на две трети.
  3. Комментируя self.pbar.setValue(step), половина времени снова.

Надеюсь, к настоящему времени должно быть очевидно, что попытка обновления ги 500 000 раз во время выполнения задачи, которая занимает меньше секунды, является массовым излишеством! В большинстве случаев реакция большинства пользователей составляет около 200 мс, поэтому вам нужно обновлять ги только один раз каждые 100 мс.

Учитывая, что один легко исправить, чтобы переместить обновления во внешнюю петлю:

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 OK, но мне нужно обновить и показать и обновить 'pbar' для моего приложения. В противном случае я не стал бы задавать вопрос :-) – saitam

+0

Извините, я не прочитал полный ответ. Внешний цикл имеет смысл, спасибо :-) Но я должен выяснить, как это сделать с потоком. – saitam

+0

@saitam. Я добавил некоторый код примера с резьбой. – ekhumoro