Busy indication with PyQt progress bar

匿名 (未验证) 提交于 2019-12-03 02:42:02

问题:

I'm trying to write a script which will display a busy indication while performing the task. And when the task is over, the progress bar will fill to the end showing that 100% task has been completed. I just want the progress bar to show a task is going on.But when I start the task, busy indication stops.It seems to me that the indication and the task can not continue together. Please help me. Here's mycode:

from PyQt4 import QtCore, QtGui from time import sleep try:     _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError:     def _fromUtf8(s):         return s  try:     _encoding = QtGui.QApplication.UnicodeUTF8     def _translate(context, text, disambig):         return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError:     def _translate(context, text, disambig):         return QtGui.QApplication.translate(context, text, disambig)  class Ui_MainWindow(object):     def setupUi(self, MainWindow):         MainWindow.setObjectName(_fromUtf8("MainWindow"))         MainWindow.resize(344, 159)         self.centralwidget = QtGui.QWidget(MainWindow)         self.centralwidget.setObjectName(_fromUtf8("centralwidget"))         self.pb = QtGui.QProgressBar(self.centralwidget)         self.pb.setGeometry(QtCore.QRect(20, 20, 301, 31))         self.pb.setProperty("value", 0)         self.pb.setObjectName(_fromUtf8("pb"))         self.btn = QtGui.QPushButton(self.centralwidget)         self.btn.setGeometry(QtCore.QRect(20, 70, 98, 27))         self.btn.setObjectName(_fromUtf8("btn"))         MainWindow.setCentralWidget(self.centralwidget)         self.menubar = QtGui.QMenuBar(MainWindow)         self.menubar.setGeometry(QtCore.QRect(0, 0, 344, 25))         self.menubar.setObjectName(_fromUtf8("menubar"))         MainWindow.setMenuBar(self.menubar)         self.statusbar = QtGui.QStatusBar(MainWindow)         self.statusbar.setObjectName(_fromUtf8("statusbar"))         MainWindow.setStatusBar(self.statusbar)          self.retranslateUi(MainWindow)         QtCore.QObject.connect(self.btn, QtCore.SIGNAL(_fromUtf8("clicked()")), self.action)         QtCore.QMetaObject.connectSlotsByName(MainWindow)                  def action(self):         self.pb.setRange(0, 0)         sleep(3) # Here I want to run a command.For example: os.system('copy something')         self.pb.setRange(0, 100)         self.pb.setValue(100)         QtGui.qApp.processEvents()      def retranslateUi(self, MainWindow):         MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))         self.btn.setText(_translate("MainWindow", "Start", None))   if __name__ == "__main__":     import sys     app = QtGui.QApplication(sys.argv)     MainWindow = QtGui.QMainWindow()     ui = Ui_MainWindow()     ui.setupUi(MainWindow)     MainWindow.show()     sys.exit(app.exec_()) 

回答1:

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 do.

def action(self):     self.pb.setRange(0, 0) # Un     sleep(3) # 

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 set 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 notify 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) 


回答2:

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_()) 


回答3:

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



回答4:

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



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