2016-05-11 4 views
1

Я хотел бы запустить индикатор в другом потоке от остальной части моего кода, но я хотел бы контролировать, как прогресс бар обновление от моего основного потока.Запуск прогресс бара в другом потоке - PySide

Является ли это то, что можно?

Это то, что я до сих пор:

import time 
from PySide import QtGui 
from PySide import QtCore 
from PySide import QtUiTools 

class progressBar(QtGui.QDialog, QtCore.QThread): 

    def __init__(self, window, title=None): 
     super(progressBar, self).__init__(window) 
     QtCore.QThread.__init__(self) 

     self.title = title or 'Progress' 
     self.setupUi() 
     self.show() 

    def setupUi(self): 
     self.setObjectName("Thinking") 
     self.gridLayout = QtGui.QGridLayout(self) 
     self.gridLayout.setObjectName("gridLayout") 
     self.progressBar = QtGui.QProgressBar(self) 
     self.gridLayout.addWidget(self.progressBar, 0, 0, 1, 1) 

     # ADJUSTMENTS 
     self.setMaximumSize(280, 50) 
     self.setMinimumSize(280, 50) 
     self.setWindowTitle(self.title) 


    def increase(self, inc): 
     self.progressBar.setProperty("value", inc) 
     time.sleep(0.01) 

    def run(self): 
     for i in range(1,101): 
      self.increase(i) 



progressThread = progressBar(QtGui.QApplication.activeWindow()) 
progressThread.start() 

Это, кажется, работает индикатор прогресса правильно внутри нити, но она управляется полностью с помощью функции выполнения.

Я попытался удалить функцию запуска и добавления этого кода в моей основной теме:

progressThread = progressBar(QtGui.QApplication.activeWindow()) 
progressThread.start() 

for i in range(1,101): 
    progressThread.increase(i) 

Но это, похоже, не работает.

Любая помощь с этим было бы здорово ... Спасибо

+1

Обратите внимание, что 'QThread's жить в потоке, который создал их (в этом случае основной поток), * не * в потоке, которыми они управляют. Этот код все еще выполняет всю свою работу в основном потоке. Вы должны вызвать метод moveToThread для панели выполнения, чтобы переместить ее в поток, управляемый объектом 'QThread'. Но также обратите внимание, что обычно не рекомендуется иметь объекты GUI в потоках, отличных от основного потока. – bnaecker

+1

У вас не может быть объектов GUI во всем, кроме основного потока/цикла событий. Вы можете отслеживать прогресс в другом потоке и отправлять сигналы в основной поток, чтобы обновить индикатор выполнения. –

+1

Вы также можете выполнить обновление через слоты и сигналы, чтобы он мог обновляться через потоки. –

ответ

0

Я считаю bnaecker, Brendan Abel и Steve Cohen уже дал вам ключевые биты информации в комментариях. Как уже говорилось, вы можете определенно запустить свой индикатор выполнения и свою логику в отдельных потоках при условии, что пользовательский интерфейс будет работать в основном потоке.

Вот пример, который должен работать так, как вы хотите:

import time, random 
import threading 

from PySide import QtCore, QtGui 


class ProgressWidget(QtGui.QWidget): 

    # just for the purpose of this example, 
    # define a fixed number of threads to run 
    nthreads = 6 

    def __init__(self): 
     super(ProgressWidget, self).__init__() 
     self.threads = [] 
     self.workers = [] 
     self.works = [0 for i in range(self.nthreads)] 
     self.setupUi() 
     self.setupWorkers() 
     self.runThreads() 

    def drawProgessBar(self): 
     self.progressBar = QtGui.QProgressBar(self) 
     self.progressBar.setGeometry(QtCore.QRect(20, 20, 582, 24)) 
     self.progressBar.minimum = 1 
     self.progressBar.maximum = 100 
     self.progressBar.setValue(0) 

    def setupUi(self): 
     self.setWindowTitle("Threaded Progress") 
     self.resize(600, 60) 
     self.drawProgessBar() 

    def buildWorker(self, index): 
     """a generic function to build multiple workers; 
     workers will run on separate threads and emit signals 
     to the ProgressWidget, which lives in the main thread 
     """ 
     thread = QtCore.QThread() 
     worker = Worker(index) 
     worker.updateProgress.connect(self.handleProgress) 
     worker.moveToThread(thread) 
     thread.started.connect(worker.work) 
     worker.finished.connect(thread.quit) 
     QtCore.QMetaObject.connectSlotsByName(self) 
     # retain a reference in the main thread 
     self.threads.append(thread) 
     self.workers.append(worker) 

    def setupWorkers(self): 
     for i in range(self.nthreads): 
      self.buildWorker(i) 

    def runThreads(self): 
     for thread in self.threads: 
      thread.start() 

    def handleProgress(self, signal): 
     """you can add any logic you want here, 
     it will be executed in the main thread 
     """ 
     index, progress = signal 
     self.works[index] = progress 
     value = 0 
     for work in self.works: 
      value += work 
     value /= float(self.nthreads) 
     # management of special cases 
     if value >= 100: 
      self.progressBar.hide() 
      return 
     # else 
     self.progressBar.setValue(value) 
     print 'progress (ui) thread: %s (value: %d)' % (threading.current_thread().name, value) 


class Worker(QtCore.QObject): 
    """the worker for a threaded process; 
    (this is created in the main thread and 
    then moved to a QThread, before starting it) 
    """ 

    updateProgress = QtCore.Signal(tuple) 
    finished = QtCore.Signal(int) 

    def __init__(self, index): 
     super(Worker, self).__init__() 
     # store the Worker index (for thread tracking 
     # and to compute the overall progress) 
     self.id = index 

    def work(self): 
     for i in range(100): 
      print 'worker thread: %s' % (threading.current_thread().name,) 
      # simulate some processing time 
      time.sleep(random.random() * .2) 
      # emit progress signal 
      self.updateProgress.emit((self.id, i + 1)) 
     # emit finish signal 
     self.finished.emit(1) 


if __name__ == "__main__": 
    import sys 
    app = QtGui.QApplication(sys.argv) 
    ui = ProgressWidget() 
    ui.show() 
    sys.exit(app.exec_()) 

Вот минимальная Разбивка:

  • объектов QThread живет в основном потоке, где они создали (не в которые они управляют)
  • для запуска задач по отдельным потокам они должны быть переданы в QThreads с использованием метода moveToThread (работники должны подкласса QObject, чтобы этот метод был доступен)
  • рабочий посылает сигналы, чтобы обновить индикатор выполнения и сообщить, что они выполнены с их задачей.
  • В этом примере мы запускаем несколько задач, каждый в своем потоке. Сигналы прогресса отправляются обратно в основной поток, где выполняется логика для обновления строки выполнения.

Замечание: на консоли рабочий выход относится к «фиктивным» потокам. Это, кажется, связано с тем, что threading модуль не имеет знания объектов QThread (это то, что я получил от here, по крайней мере). Тем не менее, кажется, достаточно доказать, что задачи рабочих выполняются на отдельных потоках. Если кто-то получит более точную информацию, не стесняйтесь расширяться.

Для тех, кто хочет узнать больше по этой теме, here is a link которой многие статьи относятся.