How to update PyQt progressbar from an independent function with arguments?

Tags: , ,



I want to use multiple imported function with arguments that takes some while to run. I want a ‘working’ progress bar that track the processes of that function. I have followed 2 questions already here.

  1. Connect an imported function to Qt5 progress bar without dependencies
  2. Report progress to QProgressBar using variable from an imported module

The difference is that the thread can take any function which can have arguments. The function also not needs to yield the percent to return to the progressbar. The progressbar always start at 0%.

I copied a snippet from first link and modified it for example purpose.

from external_script import long_running_function

class Actions(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Progress Bar')
        self.progress = QProgressBar(self)
        self.button = QPushButton('Start', self)
        self.show()

        self.button.clicked.connect(self.onButtonClick)

    def onButtonClick(self):
        long_running_function(**kwargs) # This can be any function that takes argument/s
        self.progress.setValue(value) 

Answer

Do not get too complicated with the answers as they are limited to a very particular context. In general the logic is to pass a QObject to it that updates the percentage value and then emits a signal with that value. For example a simple solution is to use the threading module:

import sys
import threading

from PyQt5 import QtCore, QtWidgets


class PercentageWorker(QtCore.QObject):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    percentageChanged = QtCore.pyqtSignal(int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._percentage = 0

    @property
    def percentage(self):
        return self._percentage

    @percentage.setter
    def percentage(self, value):
        if self._percentage == value:
            return
        self._percentage = value
        self.percentageChanged.emit(self.percentage)

    def start(self):
        self.started.emit()

    def finish(self):
        self.finished.emit()


class FakeWorker:
    def start(self):
        pass

    def finish(self):
        pass

    @property
    def percentage(self):
        return 0

    @percentage.setter
    def percentage(self, value):
        pass


import time


def long_running_function(foo, baz="1", worker=None):
    if worker is None:
        worker = FakeWorker()
    worker.start()
    while worker.percentage < 100:
        worker.percentage += 1
        print(foo, baz)
        time.sleep(1)
        worker.finish()


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.progress = QtWidgets.QProgressBar()
        self.button = QtWidgets.QPushButton("Start")

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self.button)
        lay.addWidget(self.progress)

        self.button.clicked.connect(self.launch)

    def launch(self):
        worker = PercentageWorker()
        worker.percentageChanged.connect(self.progress.setValue)
        threading.Thread(
            target=long_running_function,
            args=("foo",),
            kwargs=dict(baz="baz", worker=worker),
            daemon=True,
        ).start()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())


Source: stackoverflow