Using QThread for a throbber

2019-06-11 01:08发布

问题:

I would like to add a throbber to my GUI when some actions are launched.

Here is my script :

class StartTask(QtCore.QThread):
    taskStarted = pyqtSignal()
    def run(self):
        self.taskStarted.emit()

class StopTask(QtCore.QThread):
    taskStopped = pyqtSignal()
    def run(self):
        self.taskStopped.emit()

class Projet(object):

    def __init__(self):
        self.movie = '' # throbber
        self.startTask = StartTask()
        self.startTask.taskStarted.connect(self.startThrobber)
        self.stopTask = StopTask()
        self.stopTask.taskStopped.connect(self.stopThrobber)

    def startThrobber(self):
        # set up the movie screen on a label
        self.movie_screen = QLabel()
        # expand and center the label
        main_layout = QVBoxLayout()
        main_layout.addWidget(self.movie_screen)
        ui.throbberTab2.setLayout(main_layout)
        # use an animated gif file you have in the working folder
        byteF = QByteArray()
        movie = QMovie("D:\Various\Images\loader.gif", byteF)
        movie.setCacheMode(QMovie.CacheAll)
        movie.setSpeed(100)
        self.movie_screen.setMovie(movie)
        movie.start()

        return movie

    def stopThrobber(self):
        movie1 = self.startThrobber()
        movie1.stop()

    def goAction(self):
        if ui.chkbox.isChecked():
            self.startTask.taskStarted.connect(self.startThrobber)
            os.system(r'..........') # script launched
            self.stopTask.taskStopped.connect(self.stopThrobber)
            QMessageBox.information(self.popup(), "Information", "It works!")

Since it's the first time I'm using a Thread, I can't find what's wrong and where it is wrong..

This gives no result, even though i think i'm not too far away from the correct code.

I've managed to make the throbber appear but not at the correct moment (the thread was not working then).

回答1:

Your code has a problem in that the call to os.system() (which is presumably long running, hence the need for a throbber) is being executed in the main thread. This blocks the GUI (and the Qt event loop). QMovie requires a functioning event loop to animate correctly. So with your current code, there is nothing you can do to make the throbber appear animated, as that requires a responsive GUI (or more precisely a responsive event loop).

As an aside, you are also not allowed to directly call GUI methods from a thread. You can only emit events back to the main thread from a secondary thread (as you are currently doing). This means you cannot offload the QMovie to a thread.

So instead, you need to offload the call to os.system() into a thread, and emit a signal when it is done. This signal can be connected to a slot which stops the throbber. Immediately before launching the thread, just directly call your existing startThrobber() method to begin the throbber.

This way, your UI remains responsive (including correctly showing the throbber animation) while the thread deals with the long running process that is blocking everything up.



回答2:

Instead of using a QThread, I used subprocess which works fine. I don't know the difference though, like if one is "better" than the other, or if they have small differences etc. but it does the job !