0

I'm trying to show QProgressBar updates smoothly. The below code is filling up the progress bar from 0 to 100 triggered from a QThread, but I want to show the incremental progress update smoothly. Is there any way to do this?

import time
from PySide2.QtWidgets import QProgressBar, QApplication
from PySide2.QtCore import Slot, QThread, Signal, QObject

class ProgressBarUpdater(QThread):
    progressBarValue = Signal(int)

    def run(self):
        value: int = 0
        while value <= 100:
            time.sleep(0.1)
            self.progressBarValue.emit(value)
            value += 5


def main():
    app = QApplication([])
    pbar = QProgressBar()
    pbar.setMinimum(0)
    pbar.setMaximum(100)
    pbar.show()

    @Slot(int)
    def progressbar_update(value: int):
        pbar.setValue(value)

    progressBarUpdater = ProgressBarUpdater()
    progressBarUpdater.progressBarValue.connect(progressbar_update)
    progressBarUpdater.start()

    app.exec_()

if __name__ == "__main__":
    main()
  • Your question is unclear: you're emitting values with steps of 5 units, so it can't be "smooth". Also, QThread already inherits from QObject, so you don't need `__SignalEmitter` to add a signal, just add it to the `ProgressBarUpdater` class (you might be confused by QRunnable, which does *not* inherit from QObject). – musicamante Sep 13 '21 at 12:10
  • Thanks for pointing that out and I'll edit the question to make it more clear. Actually, I want to make the upgrade of the progress bar smoothly animated using QPropertyAnimation. Is it possible? – Rizwan Hasan Sep 13 '21 at 14:12
  • The problem is more conceptual. An animation requires 3 things: a start value, an end value, and an interval for its duration. Unless you know the exact amount of the end value (the next step) **and** at least a reliable estimate of the duration, you cannot create an animation. Even supposing that you know for certain that the increase will always be by 5 units, how can you know how much time will it take to arrive there? What if the thread cycle requires more time? Or what if the cycle finishes before the interval? – musicamante Sep 13 '21 at 14:16
  • Note that this doesn't mean that *nothing* could be done. For instance, if you know that the process timing is usually consistent in its computation, you can create a system that starts using a animations from the *current* value to the *new* one, and as soon as you have enough data you can create new animations using estimates based on that data. The drawback is that the visible progress is unreliable: suppose you're doing a network request and the network goes off, the result is that the user will think that the process is still working (and is possibly completed), but in reality it's stuck. – musicamante Sep 13 '21 at 14:33
  • Thanks. I've got your point and I'm satisfied with your brief explanations. – Rizwan Hasan Sep 13 '21 at 14:44
  • I've got it working. I faked it to look like some actual process by adding random sleep time and random forwarding steps. Without using QPropertyAnimation it looks very straight progress, but using the QPropertyAnimation it looks a lot smoother than straight. [https://stackoverflow.com/questions/24645585/how-to-show-qprogressbar-smoothly](https://stackoverflow.com/questions/24645585/how-to-show-qprogressbar-smoothly) – Rizwan Hasan Sep 13 '21 at 15:17
  • Good for you, if your solution works fine for your needs, consider posting it as your own answer. – musicamante Sep 13 '21 at 15:22

1 Answers1

3

You can use a QPropertyAnimation to update the values:

import time

from PySide2.QtCore import QPropertyAnimation, QThread, Signal
from PySide2.QtWidgets import QProgressBar, QApplication


class ProgressBarUpdater(QThread):
    progressBarValue = Signal(int)

    def run(self):
        value: int = 0
        while value <= 100:
            time.sleep(0.1)
            self.progressBarValue.emit(value)
            value += 5


class ProgressBar(QProgressBar):
    def update_value(self, value, animated=True):
        if animated:
            if hasattr(self, "animation"):
                self.animation.stop()
            else:
                self.animation = QPropertyAnimation(
                    targetObject=self, propertyName=b"value"
                )
                self.animation.setDuration(100)
            self.animation.setStartValue(self.value())
            self.animation.setEndValue(value)
            self.animation.start()
        else:
            self.setValue(value)


def main():
    app = QApplication([])
    pbar = ProgressBar()
    pbar.setMinimum(0)
    pbar.setMaximum(100)
    pbar.show()

    progressBarUpdater = ProgressBarUpdater()
    progressBarUpdater.progressBarValue.connect(pbar.update_value)
    progressBarUpdater.start()

    app.exec_()


if __name__ == "__main__":
    main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241