Python PyQt 进度条忙指示
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19442443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Busy indication with PyQt progress bar
提问by user2420437
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:
我正在尝试编写一个脚本,该脚本将在执行任务时显示忙碌指示。当任务结束时,进度条会填充到最后,显示任务已完成 100%。我只是想让进度条显示一个任务正在进行。但是当我开始任务时,忙指示停止了。在我看来,指示和任务不能一起继续。请帮我。这是我的代码:
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_())
采纳答案by Yoann Quenach de Quivillic
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.
首先,直接编辑用 QtDesigner 创建的代码是个坏主意。您可能已经看到# WARNING! All changes made in this file will be lost!
文档顶部的一行。对于这样一个简单的小部件,最好使用手动编码。
Secondly, take a closer look at what the action
slot actually does.
其次,仔细看看action
插槽的实际作用。
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.
当您的插槽被阻止时,您的 progressBar 没有理由更新其值sleep
。当action
被调用时,槽线程休眠 3 秒,然后将进度条设置为完整的 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 pulsedProgressBar 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.
您不能指望progressBar 在您的任务运行时神奇地自我更新。如果您不知道需要多长时间并且无法按步骤细分,则应考虑改用脉冲ProgressBar(请参见下面的示例 1)。如果您可以轻松获得任务的进度(例如复制 n 个文件),则应相应地更新 progressBar 的值。
Either way, you should use QThread
to get a non-blocking behaviour, and signals
to communicate between your thread(s) and your main application.
无论哪种方式,您都应该使用QThread
来获得非阻塞行为,并signals
在您的线程和主应用程序之间进行通信。
- 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
- 主应用程序启动执行长时间运行任务的 QThread。
- QThread 通知主应用程序的任务进展(如果可用)或完成
Example 1 - Pulse ProgressBar:
示例 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.
如果最小值和最大值都设置为 0,则进度条将显示忙指示符而不是步数百分比。
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:
示例 2 - 经典进度条:
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)
回答by 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:
最后我得到了我想要的东西,虽然需要一点点编辑。我刚刚将此行添加到 onFinished(): self.progressBar.setValue(1) 以确认 100% 任务完成。这是代码:
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_())
回答by Arturo Morales Rangel
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
这个设置会有一个很忙的样子,你不需要把它添加到任何函数中,如果你愿意,它可以在类构造函数中
回答by daegontaven
A bit late, but I've written detailed documentation on this very issue since a lot of people seem to face this problem.
有点晚了,但我已经写了关于这个问题的详细文档,因为很多人似乎都面临这个问题。