Busy indication with PyQt progress bar

主宰稳场 提交于 2019-11-27 12:15:15

First, it's a bad idea to directly edit the code created with QtDesigner. You may have seen the line # WARNING! All changes made in this file will be lost! at the top of the document. For such a simple widget, you're better off with manual coding.

Secondly, take a closer look at what the action slot actually does.

def action(self):
    self.pb.setRange(0, 0) # Un
    sleep(3) # <-- Your slot blocks HERE
    self.pb.setRange(0, 100)
    self.pb.setValue(100)
    QtGui.qApp.processEvents()

There is no reason for your progressBar to update its value while your slot is blocked in sleep. When action is called, the slot thread sleeps for 3 sec then sets the progress bar to a full 100.

You can't expect the progressBar to magically update itself while your task is running. If you have no idea how long it will take and you can't subdivise it in steps, you should consider using a pulsed ProgressBar instead (see example 1 below). If you can easily get the progression of your task (say copying n files), you should update the value of your progressBar accordingly.

Either way, you should use QThread to get a non-blocking behaviour, and signals to communicate between your thread(s) and your main application.

  • The main application starts the QThread implementing the long-running task.
  • The QThread notifies the task progression (if available) or completion to the main application

Example 1 - Pulse ProgressBar:

If minimum and maximum are both set to 0, the progress bar will show a busy indicator instead of a percentage of steps.

class MyCustomWidget(QtGui.QWidget):

    def __init__(self, parent=None):
        super(MyCustomWidget, self).__init__(parent)
        layout = QtGui.QVBoxLayout(self)

        # Create a progress bar and a button and add them to the main layout
        self.progressBar = QtGui.QProgressBar(self)
        self.progressBar.setRange(0,1)
        layout.addWidget(self.progressBar)
        button = QtGui.QPushButton("Start", self)
        layout.addWidget(button)      

        button.clicked.connect(self.onStart)

        self.myLongTask = TaskThread()
        self.myLongTask.taskFinished.connect(self.onFinished)

    def onStart(self): 
        self.progressBar.setRange(0,0)
        self.myLongTask.start()

    def onFinished(self):
        # Stop the pulsation
        self.progressBar.setRange(0,1)


class TaskThread(QtCore.QThread):
    taskFinished = QtCore.pyqtSignal()
    def run(self):
        time.sleep(3)
        self.taskFinished.emit()  

Example 2 - Classic ProgressBar:

class MyCustomWidget(QtGui.QWidget):

    def __init__(self, parent=None):
        super(MyCustomWidget, self).__init__(parent)
        layout = QtGui.QVBoxLayout(self)       

        self.progressBar = QtGui.QProgressBar(self)
        self.progressBar.setRange(0,100)
        button = QtGui.QPushButton("Start", self)
        layout.addWidget(self.progressBar)
        layout.addWidget(button)

        button.clicked.connect(self.onStart)

        self.myLongTask = TaskThread()
        self.myLongTask.notifyProgress.connect(self.onProgress)


    def onStart(self):
        self.myLongTask.start()

    def onProgress(self, i):
        self.progressBar.setValue(i)


class TaskThread(QtCore.QThread):
    notifyProgress = QtCore.pyqtSignal(int)
    def run(self):
        for i in range(101):
            self.notifyProgress.emit(i)
            time.sleep(0.1)
user2420437

Finally I got what I wanted, though little bit editing needed . I just added this line to onFinished(): self.progressBar.setValue(1) to confirm 100% task completion. Here's the code:

from PyQt4 import QtCore, QtGui
from time import sleep
import sys, os

class MyCustomWidget(QtGui.QWidget):

    def __init__(self, parent=None):
        super(MyCustomWidget, self).__init__(parent)
        layout = QtGui.QVBoxLayout(self)

        # Create a progress bar and a button and add them to the main layout
        self.progressBar = QtGui.QProgressBar(self)
        self.progressBar.setRange(0,1)
        layout.addWidget(self.progressBar)
        button = QtGui.QPushButton("Start", self)
        layout.addWidget(button)      

        button.clicked.connect(self.onStart)

        self.myLongTask = TaskThread()
        self.myLongTask.taskFinished.connect(self.onFinished)

    def onStart(self): 
        self.progressBar.setRange(0,0)
        self.myLongTask.start()

    def onFinished(self):
        # Stop the pulsation
        self.progressBar.setRange(0,1)
        self.progressBar.setValue(1)


class TaskThread(QtCore.QThread):
    taskFinished = QtCore.pyqtSignal()
    def run(self):
        os.system('sudo apt-get install leafpad')
        self.taskFinished.emit() 

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MyCustomWidget()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

A bit late, but I've written detailed documentation on this very issue since a lot of people seem to face this problem.

Introduction to Progress Bars

apply this:

progressbar.setMinimum(0)
progressbar.setMaximum(0)
progressbar.setValue(0)

This setting will have a busy appearance, You do not need to add it to any function, it can be in the class constructor if you want

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!