在另一个线程中运行进度条 - Pyside

时间:2016-05-11 23:22:49

标签: python pyqt pyside qthread nuke

我想在与其余代码不同的线程中运行进度条,但我想控制进度条如何从主线程更新。

这是可能的吗?

这是我到目前为止所做的:

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

这似乎是在线程内部正确运行进度条,但它完全由运行函数控制。

我尝试删除run函数并将此代码添加到我的主线程中:

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

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

但这似乎不起作用。

对此的任何帮助都会很棒......谢谢

1 个答案:

答案 0 :(得分:1)

我相信bnaeckerBrendan AbelSteve Cohen已经在评论中向您提供了关键信息。 正如他们已经说过的那样,只要UI在主线程上运行,你就可以在单独的线程中运行你的进度条和你的逻辑。

这是一个应该按照你想要的方式运作的例子:

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

这是一个最小的细分:

  • QThreads存在于他们创建的主线程中(不在他们管理的线程中)
  • 为了在不同的线程上运行任务,必须使用moveToThread方法将它们传递给QThreads(工作者需要子类QObject才能使该方法可用)
  • 工作人员发送信号以更新进度条并通知他们完成任务
  • 在这个例子中,我们正在运行多个任务,每个任务都在自己的线程中。进度信号被发送回主线程,其中更新进度条的逻辑运行

旁注:在控制台中,工人'输出是指" dummy"线程。这似乎与threading模块不了解QThreads(这是我从here获得的信息这一事实有关)。 但是,似乎足以证明工人'任务在不同的线程上运行。如果有人获得更准确的信息,请随意扩展。

对于那些想要阅读更多关于此主题的人,here is a link许多文章都提到了这些内容。