Python PySide and Progress Bar Threading

前端 未结 2 601
梦毁少年i
梦毁少年i 2020-12-02 11:51

I have this code:

from PySide import QtCore, QtGui
import time

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName(\"Dialog         


        
相关标签:
2条回答
  • 2020-12-02 12:10

    It's a mistake to think you always need to use multi-threading for things like this.

    If you can break up your long-running task into a series of small steps, all you need to do is ensure that any pending events are processed sufficiently often for the GUI to remain responsive. This can be done safely from the within the main GUI thread by using processEvents, like this:

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

    Given it's simplicity, it's always worth at least considering this technique before opting for a much more heavyweight solution like multi-threading or multi-processing.

    0 讨论(0)
  • 2020-12-02 12:17

    I think you may be mistaken. You want the work you're doing in a separate thread so it doesn't freeze the application. But you also want to be able to update the progress bar. You can achieve this by creating a worker class using a QThread. QThreads are able emit signals, which your UI can listen for and act appropriately.

    First, let's create your worker class.

    #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)
    

    So now that you have a worker class, it's time to make use of it. You will want to create a new function in your Ui_Dialog class to handle the emitted signals.

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

    While you're there, you can remove your progress() function.

    in retranslateUi() you will want to update the push button event handler from

    self.pushButton.clicked.connect(self.progress)
    

    to

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

    Finally, in your setupUI() function, you will need to create an instance of your worker class and connect it's signal to your setProgress() function.

    Before this:

    self.retranslateUi(Dialog)
    

    Add this:

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

    Here is the final code:

    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 have some built in signals that are automatically emitted. You can see them, and more information about QThreads in the documentation

    0 讨论(0)
提交回复
热议问题