0

So I have this part of code which does a simple thing : it launches a script and while the script is processing, a throbber is set on.

   def go(self):
        if ui.chk.isChecked():
            self.startThrobber()
            script = subprocess.check_call(r'"C:\Program Files\FME\fme.exe"', shell=False)

            if script == 0: 
                self.stopThrobber() # opens a QMessageBox and stops throbber
        else:
            QMessageBox.information(self.popup(), "Errpr", "Error !")

After trying different methods (QThread, subprocess.Popen ...) this is the closest i got to make it work.

The only thing that doesn't work is that the throbber doesn't start right before the subprocess is executed, it starts after and thus it never stops.

So why is the throbber not ending when stopThrobber() is executed ? And why is startThrobber not being executed before the subprocess (i'm pretty sure it's a subprocess thing, but i'm pretty new to all this, never heard about thread until yesterday)

EDIT : The single quote was just a typing error, sorry. Still doesn't fix the problem.

guy16
  • 233
  • 2
  • 3
  • 16
  • Your single quotes denoting the raw string enclose the 'shell' argument. Move your closing single-quote to between the ending double-quote and the comma – JS. Jun 01 '15 at 17:22
  • Doesn't this method have exactly the same problem as your [previous method](http://stackoverflow.com/q/30532340/1994235), as `subprocess.check_call()` is a blocking call? Thus the throbber does not animate correctly while the subprocess is running because control is not being returned to the Qt event loop? – three_pineapples Jun 01 '15 at 23:19
  • @three_pineapples yes still the same problem, but since I used a different method I thought it's best to ask a new question – guy16 Jun 02 '15 at 07:42
  • @JS. Yep sorry, that was just a typing mistake I did.. It's correct in my code, and it's not working – guy16 Jun 02 '15 at 07:45

3 Answers3

2

Any call to a subprocess, from your main thread, that is blocking (waits for return) is going to stop your throbber from working properly. My answer to your other SO question on this topic outlines an approach that does not cause the subprocess call to block the main thread. I should point out that solution is not the only way to create a non-blocking call to a subprocess (for instance see here. You could create a QTimer to poll the subprocess poll() method periodically so that you can check the returncode to see if the subprocess has finished.)

The key theme is that you need your methods that run in the main thread to return quickly in order to keep the GUI responsive and allow your throbber to run/animate. So choose a way to launch the subprocess that meets this requirement.

Community
  • 1
  • 1
three_pineapples
  • 11,579
  • 5
  • 38
  • 75
  • I didn't manage to make it work using a QThread so I turned to subprocess (thinking it would be easier, alas..) I don't think i can use a QTimer since i don't know how long the subprocess will take to execute I've tried subprocess.Popen and subprocess.check_call : the first one starts the throbber but doesn't stops it and the latter the other way around.... – guy16 Jun 02 '15 at 08:50
  • @guy16 Sorry, I made a mistake in what I wrote. You could configure a `QTimer` to call the `poll()` method of the subprocess, and then check the subprocess returncode to see if it has finished. If not, queue up the `QTimer` again. Rinse, repeat. I've updated my answer accordingly. – three_pineapples Jun 02 '15 at 12:09
  • I have a while loop that checks if poll() is None or not, if not a signal is send to stop the throbber BUT now startThrobber() doesn't work.. I tried to put in the loop, before the loop or leave it in the main thread but it's the same.. – guy16 Jun 02 '15 at 16:22
  • @guy16. A while loop will just block. Use a `QTimer` to do the polling, as has already been suggested. – ekhumoro Jun 02 '15 at 17:08
  • The loop doesnt seem to block it, i put a timer that checks every second if the process is over. It's before the loop that the function startThrobber is not executed for some reason – guy16 Jun 02 '15 at 17:17
0

Your single quotes denoting the raw string enclose the 'shell' argument.

def go(self):
        if ui.chk.isChecked():
            self.startThrobber()
            script = subprocess.check_call(r"C:\Program Files\FME\fme.exe", shell=False)

            if script == 0: 
                self.stopThrobber() # opens a QMessageBox and stops throbber
        else:
            QMessageBox.information(self.popup(), "Errpr", "Error !")
alexisdevarennes
  • 5,437
  • 4
  • 24
  • 38
  • 1
    Your answer should explain what changes you have made to the original code, and why they fix the problem. Also note that you have introduced a bug by not using a raw string for the path (i.e. `\f` is a form-feed character). – ekhumoro Jun 01 '15 at 16:30
  • Oops sorry, that was just a typing mistake. In my code it's in the correct way and there is still a problem – guy16 Jun 02 '15 at 07:44
0

So I've tried another thing (unsuccessfully..) When I click on a button, it executes startThrobber() and sends a signal to the following function :

def go(self):

    self.startThrobber()

    script = subprocess.Popen(r'"C:\Program Files\FME\fme.exe" ', shell=False)

    while script.poll() == None:
        time.sleep(1)
    else:
        p.stopThrobber()

But still doesn't working.. startThrobber is executed but nothing appears on the GUI... I thougth that the point of subprocess was to allow to do multiple tasks simultaneously so why isn't the throbber appearing ?

UPDATE : if i erase the while loop, startThrobber works : it appears while the subprocess is turning. So why when there is the while loop it doesn't work ?!

guy16
  • 233
  • 2
  • 3
  • 16
  • As I said before, the while loop will block the GUI. Any queued events won't be processed until the function exits and control returns to the event-loop. You can force processing of the queued events by calling `QtGui.qApp.processEvents()` inside the while loop. But the preferred solution is to use a `QTimer`, which is designed for exactly this kind of use-case. – ekhumoro Jun 03 '15 at 16:39
  • @ekhumoro if i get it right, instead of a while loop, i use a QTimer and the timeout signal connected to stopThrobber ? – guy16 Jun 04 '15 at 08:22
  • The timer would be given an interval of, say, 200ms. It will then emit the `timeout` signal every 200ms, allowing a slot connected to it to poll the script (once) to see if it's finished. If it is, it can stop the throbber (and, of course, stop the timer). The elements of the code are actually very similar to what you have already, but using a `QTimer` should prevent blocking of the GUI. – ekhumoro Jun 04 '15 at 22:21