【问题标题】:Busy indication with PyQt progress barPyQt 进度条的忙碌指示
【发布时间】:2013-10-26 20:54:27
【问题描述】:

我正在尝试编写一个显示忙碌指示的脚本 在执行任务时。而当任务结束时,进度条 将填充到最后,显示 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_())

【问题讨论】:

    标签: python pyqt pyqt4 progress


    【解决方案1】:

    首先,直接编辑使用 QtDesigner 创建的代码是个坏主意。您可能已经在文档顶部看到了 # WARNING! All changes made in this file will be lost! 行。对于这样一个简单的小部件,您最好手动编码。

    其次,仔细看看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()
    

    当您的插槽在sleep 中被阻止时,您的progressBar 没有理由更新其值。当调用action 时,槽线程休眠 3 秒,然后将进度条设置为满 100。

    您不能指望progressBar 在您的任务运行时神奇地自我更新。如果您不知道需要多长时间并且无法按步骤细分,则应考虑使用 pulsed ProgressBar(参见下面的示例 1)。如果您可以轻松获得任务的进度(例如复制 n 个文件),则应相应地更新 progressBar 的值。

    无论哪种方式,您都应该使用QThread 来获得非阻塞行为,并使用signals 在您的线程和主应用程序之间进行通信。

    • 主应用程序启动 QThread 执行长时间运行的任务。
    • QThread 将任务进度(如果可用)或完成通知给主应用程序

    示例 1 - Pulse ProgressBar:

    如果最小值和最大值都设置为 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()  
    

    示例 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)
    

    【讨论】:

    • 感谢您的精彩解释。实际上,我以前从未尝试过线程。请告诉我运行 gui 的结束代码。我怀疑它不会像常见的类型: if name == "main": MainWindow = QtGui.QMainWindow() ...等。我说的对吗?
    • 有没有办法在没有新类的情况下更新进度条? def longtask() startprogressbar() logtask ... endprogressbar() 之类的东西?
    • 谢谢。你的回答真的很有帮助。我试图实现它来解决另一个问题,但不知何故我失败了。你能看看我的问题,看看你能不能帮我解决这个问题?这是链接:stackoverflow.com/questions/47256606/…
    【解决方案2】:

    最后我得到了我想要的,虽然需要一点点编辑。我刚刚添加了这一行 到 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_())
    

    【讨论】:

      【解决方案3】:

      有点晚了,但我已经写了关于这个问题的详细文档,因为很多人似乎都面临这个问题。

      Introduction to Progress Bars

      【讨论】:

        【解决方案4】:

        应用这个:

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

        这个设置会有一个忙碌的样子,你不需要添加到任何函数中,如果你想可以在类构造函数中

        【讨论】:

          猜你喜欢
          • 2017-05-25
          • 1970-01-01
          • 2018-11-04
          • 2017-04-08
          • 1970-01-01
          • 2012-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多