运行 不同线程中的进度条 - Pyside

Running progress bar in a different thread - 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)

但这似乎不起作用。

任何帮助都将非常有用...谢谢

我相信 , and 已经在他们的评论中为您提供了关键信息。 正如他们已经说过的那样,如果主线程上有 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 才能使该方法可用)
  • 工作人员发送信号以更新进度条并通知他们完成任务
  • 在这个例子中,我们 运行 执行多个任务,每个任务都在自己的线程中。进度信号被发送回主线程,其中更新进度条的逻辑运行s

旁注:在控制台中,工作人员的输出指的是 "dummy" 个线程。这似乎与 threading 模块不了解 QThreads 这一事实有关(至少我是从 here 那里得到的)。 不过,这似乎足以证明工作人员的任务 运行 在不同的线程上进行。如果谁有更准确的信息,欢迎补充。

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