0

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

three_pineapples
  • 11,579
  • 5
  • 38
  • 75
guy16
  • 233
  • 2
  • 3
  • 16
  • Why are you trying to use threads? Nothing there seems to need a thread. The threads you have created simply emit a single signal when they are started, and that's it (the thread ends). Threads are meant for long running background tasks, which doesn't seem to be what you are doing here. – three_pineapples May 30 '15 at 01:49
  • Alright. So how can I use the throbber ? Because it's not set on while the script is running, it's only on when the script has finished running. I thought there was a special way for doing this ? – guy16 Jun 01 '15 at 07:27

2 Answers2

2

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.

three_pineapples
  • 11,579
  • 5
  • 38
  • 75
  • Oh yes, I hadn't thought of doing it this way! Thank you, i'll correct my code !! – guy16 Jun 01 '15 at 09:08
  • I've tried this but I still have the same problem. The throbber doesn't begin before i call the thread, and moreover, the throbber doesn't stop (eventhough there is a movie.stop() command).. – guy16 Jun 01 '15 at 10:11
  • @guy16 It might be easier to post a new question now that you are taking a different approach with a [minimilistic working example](http://stackoverflow.com/help/mcve) demonstrating the new problem. – three_pineapples Jun 01 '15 at 10:15
  • "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." That is where i have trouble. I cannot manage to make both work, it's either the throbber would start but wont stop or the other way round. Because in the thread, where i have the subprocess.Popen and then a signal to stop the throbber, it does those 2 actions simultaneously.. HHow can I make the signal wait to emit without using a timer ? – guy16 Jun 02 '15 at 10:21
  • @guy16 How long does it take for the command you are executing to run normally? (aka if you ran it from the command line) – three_pineapples Jun 02 '15 at 12:04
  • it really depends on the input data, it ranges from 1 min to 20 min – guy16 Jun 02 '15 at 12:32
  • An example with code snippet could've helped lot of us noobs using qthreads. – answerSeeker Feb 18 '17 at 20:32
  • @answerSeeker There are plenty of minimal examples on stack overflow, demonstrating how to use a QThread for various tasks. For instance [here](http://stackoverflow.com/documentation/pyqt/2775/using-threads-with-pyqt#t=20170219051437040823), [here](http://stackoverflow.com/a/35534047/1994235) and [here](http://stackoverflow.com/a/21071865/1994235). If you have a specific issue, I would encourage you to [ask a new question](http://stackoverflow.com/questions/ask). – three_pineapples Feb 19 '17 at 05:18
  • @three_pineapples, thanks for the examples. I gained a lot more clarity! – answerSeeker Feb 19 '17 at 09:40
0

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 !

guy16
  • 233
  • 2
  • 3
  • 16