【问题标题】:Python PySide and Progress Bar ThreadingPython PySide 和进度条线程
【发布时间】:2014-01-06 14:35:48
【问题描述】:

我有这个代码:

from PySide import QtCore, QtGui
import time

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(400, 133)
        self.progressBar = QtGui.QProgressBar(Dialog)
        self.progressBar.setGeometry(QtCore.QRect(20, 10, 361, 23))
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.pushButton = QtGui.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(20, 40, 361, 61))
        self.pushButton.setObjectName("pushButton")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setText(QtGui.QApplication.translate("Dialog", "PushButton", None, QtGui.QApplication.UnicodeUTF8))
        self.progressBar.setValue(0)
        self.pushButton.clicked.connect(self.progress)

    def progress(self):
        self.progressBar.minimum = 1
        self.progressBar.maximum = 100
        for i in range(1, 101):
            self.progressBar.setValue(i)
            time.sleep(0.1)

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    Dialog = QtGui.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())

我希望将进度条放在单独的线程中,这样它就不会冻结应用程序,但我似乎找不到如何做到这一点。

有人可以帮忙吗?

【问题讨论】:

    标签: python multithreading qt pyside


    【解决方案1】:

    我想你可能弄错了。你希望你正在做的工作在一个单独的线程中,这样它就不会冻结应用程序。但是您还希望能够更新进度条。您可以通过使用QThread 创建一个工人类来实现这一点。 QThreads 能够发出信号,您的 UI 可以监听并采取适当的行动。

    首先,让我们创建你的工人阶级。

    #Inherit from QThread
    class Worker(QtCore.QThread):
    
        #This is the signal that will be emitted during the processing.
        #By including int as an argument, it lets the signal know to expect
        #an integer argument when emitting.
        updateProgress = QtCore.Signal(int)
    
        #You can do any extra things in this init you need, but for this example
        #nothing else needs to be done expect call the super's init
        def __init__(self):
            QtCore.QThread.__init__(self)
    
        #A QThread is run by calling it's start() function, which calls this run()
        #function in it's own "thread". 
        def run(self):
            #Notice this is the same thing you were doing in your progress() function
            for i in range(1, 101):
                #Emit the signal so it can be received on the UI side.
                self.updateProgress.emit(i)
                time.sleep(0.1)
    

    现在你有了一个工人阶级,是时候利用它了。您需要在 Ui_Dialog 类中创建一个新函数来处理发出的信号。

    def setProgress(self, progress):
        self.progressBar.setValue(progress)
    

    当你在那里时,你可以删除你的 progress() 函数。

    retranslateUi() 中,您需要从

    更新按钮事件处理程序
    self.pushButton.clicked.connect(self.progress)
    

    self.pushButton.clicked.connect(self.worker.start)
    

    最后,在您的setupUI() 函数中,您需要创建一个工作类实例并将其信号连接到您的setProgress() 函数。

    在此之前:

    self.retranslateUi(Dialog)
    

    添加这个:

    self.worker = Worker()
    self.worker.updateProgress.connect(self.setProgress)
    

    这是最终代码:

    from PySide import QtCore, QtGui
    import time
    
    
    class Ui_Dialog(object):
        def setupUi(self, Dialog):
            Dialog.setObjectName("Dialog")
            Dialog.resize(400, 133)
            self.progressBar = QtGui.QProgressBar(Dialog)
            self.progressBar.setGeometry(QtCore.QRect(20, 10, 361, 23))
            self.progressBar.setProperty("value", 24)
            self.progressBar.setObjectName("progressBar")
            self.pushButton = QtGui.QPushButton(Dialog)
            self.pushButton.setGeometry(QtCore.QRect(20, 40, 361, 61))
            self.pushButton.setObjectName("pushButton")
    
            self.worker = Worker()
            self.worker.updateProgress.connect(self.setProgress)
    
            self.retranslateUi(Dialog)
            QtCore.QMetaObject.connectSlotsByName(Dialog)
    
            self.progressBar.minimum = 1
            self.progressBar.maximum = 100
    
        def retranslateUi(self, Dialog):
            Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
            self.pushButton.setText(QtGui.QApplication.translate("Dialog", "PushButton", None, QtGui.QApplication.UnicodeUTF8))
            self.progressBar.setValue(0)
            self.pushButton.clicked.connect(self.worker.start)
    
        def setProgress(self, progress):
            self.progressBar.setValue(progress)
    
    #Inherit from QThread
    class Worker(QtCore.QThread):
    
        #This is the signal that will be emitted during the processing.
        #By including int as an argument, it lets the signal know to expect
        #an integer argument when emitting.
        updateProgress = QtCore.Signal(int)
    
        #You can do any extra things in this init you need, but for this example
        #nothing else needs to be done expect call the super's init
        def __init__(self):
            QtCore.QThread.__init__(self)
    
        #A QThread is run by calling it's start() function, which calls this run()
        #function in it's own "thread". 
        def run(self):
            #Notice this is the same thing you were doing in your progress() function
            for i in range(1, 101):
                #Emit the signal so it can be received on the UI side.
                self.updateProgress.emit(i)
                time.sleep(0.1)
    
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        Dialog = QtGui.QDialog()
        ui = Ui_Dialog()
        ui.setupUi(Dialog)
        Dialog.show()
        sys.exit(app.exec_())
    

    QThreads 有一些自动发出的内置信号。您可以查看它们,以及有关 QThreads 的更多信息in the documentation

    【讨论】:

    • 如果不锁定 GUI,这并不完美。例如,如果您在进程栏更新时打开 QFileDialog。它只会停止 QThread
    【解决方案2】:

    认为您总是需要为此类事情使用多线程是错误的。

    如果您可以将长时间运行的任务分解为一系列小步骤,那么您需要做的就是确保足够频繁地处理任何未决事件,以使 GUI 保持响应。这可以通过使用processEvents 在主GUI 线程 安全地完成,如下所示:

        for i in range(1, 101):
            self.progressBar.setValue(i)
            QtGui.qApp.processEvents()
            time.sleep(0.1)
    

    鉴于它的简单性,在选择更重量级的解决方案(如多线程或多处理)之前,至少考虑一下这种技术总是值得的。

    【讨论】:

    • 注意,time.sleep 仅用于测试目的。不适合Qt
    猜你喜欢
    • 1970-01-01
    • 2016-09-07
    • 1970-01-01
    • 2011-06-21
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    相关资源
    最近更新 更多