【问题标题】:Running progress bar in a different thread - Pyside在不同的线程中运行进度条 - Pyside
【发布时间】:2016-09-07 13:47:00
【问题描述】:

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

这是可能的吗?

这是我目前所拥有的:

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 函数控制。

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

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

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

但这似乎不起作用。

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

【问题讨论】:

  • 请注意,QThreads 存在于创建它们的线程中(在本例中为主线程),在它们管理的线程中。这段代码仍然在主线程中完成所有工作。您应该调用进度条的moveToThread 方法将其移动到由QThread 对象管理的线程。但也要注意,在主线程以外的线程中拥有 GUI 对象通常不是一个好主意。
  • 除了主线程/事件循环之外,您不能有 GUI 对象。您可以在另一个线程中跟踪进度并向主线程发送信号以更新进度条。
  • 您还可以通过槽和信号进行更新,以允许跨线程更新。

标签: python pyqt pyside qthread nuke


【解决方案1】:

我相信bnaeckerBrendan AbelSteve Cohen 已经在他们的 cmets 中为您提供了关键信息。 正如他们已经说过的,如果 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_())

这是一个最小的细分:

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

附注:在控制台中,worker 的输出指的是“虚拟”线程。这似乎与threading 模块不了解QThreads 的事实有关(至少我从here 得到的信息)。 尽管如此,这似乎足以证明工人的任务在不同的线程上运行。如果有人有更准确的信息,请随时扩展。

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

【讨论】:

    猜你喜欢
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-19
    • 2023-03-28
    • 2012-04-05
    • 2011-01-07
    相关资源
    最近更新 更多