1

I have a python programm that I want a progressbar to be pulsed when I run a bash script in a thread and show some messages.

When I click a button, the thread is started launching the script, but the messages and progressbar only gets responsive when the thread is finished, showing only the last message.

Reading, I understood that I'm blocking the main loop but I can't figure it out how to solve this.

Simplified code of my program, the problem is when calling "on_fixme_button_pressed":

from gi.repository import Gtk, Gdk, Pango, GObject, GLib
import os, sys
import xml.etree.ElementTree as etree
from urllib.request import urlopen
from subprocess import Popen
import threading


UI_FILE = "remendo_gtk.ui"
#UI_FILE = "/usr/local/share/remendo_gtk/ui/remendo_gtk.ui"

GObject.threads_init()

class GUI:

    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file(UI_FILE)
        self.builder.connect_signals(self)

        self.window = self.builder.get_object('remendo')
        self.event_treeview = self.builder.get_object('event_treeview')
        self.event_info = self.builder.get_object('event_info')
        self.progressbar = self.builder.get_object('progressbar')
        self.progressbar_lock = threading.Lock()

        self.selected_event = ''
        self.url_script = ''
        self.local_script = ''
        self.window.connect("destroy", lambda _: Gtk.main_quit())
        self.set_event_list()

        self.window.show_all()

    def pulse_progressbar(self):
        if threading.active_count() > 1:
            self.progressbar.pulse()
            return True

        self.progressbar.set_fraction(0.0)
        self.progressbar_lock.release()
        return False

    def on_fixme_button_clicked(self, button):
        self.event_info.set_label("Fixing now...")
        script = threading.Thread(target=self.run_script(), args=(self,))
        script.start()

        if self.progressbar_lock.acquire(False):
            GLib.timeout_add(250, self.pulse_progressbar)

    def run_script(self):
        try:
            self.local_script = '/tmp/%s.sh' % self.selected_event.replace(" ", "_")
            script = urlopen(self.url_script)
            localscript = open(self.local_script, 'wb')
            localscript.write(script.read())
            localscript.close()

            command = ['bash', self.local_script]
            p = Popen(command)
            p.communicate()

            #self.event_solved()
            self.event_info.set_label("My work is done. Good day!")
        except:
            self.event_info.set_label("Script failed: %s" % self.local_script)

        return False


def main():
    app = GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())
5
  • Popen isn't a regular function, but rather a constructor. If you want to do anything with the process you opened, you'll need to store its return-value in a variable that you can interact with and call methods on (such as wait). Commented Jun 15, 2013 at 20:14
  • Yes, using .communicate() seems to work just writting p=Popen(command) and then p.communicate(), but for some reason the popup window doesn't show up until the command ends, showing the "My work is done" message Commented Jun 15, 2013 at 20:54
  • I suggest posting your updated code. Commented Jun 16, 2013 at 3:05
  • Here's the updated code (pastebin.com/xRqb9uDQ). I removed the popup window to work only on the main window. The code is supposed to pulse the progressbar when a thread is running, but it keeps freezing until the thread is finished. I think I understand that it is because the main loop is blocked, but I can't figure it out how to solve it. The main problem is within the on_fixme_button_clicked call Commented Jun 16, 2013 at 11:34
  • 1
    If you don't want the main process to block, then you shouldn't be calling the communicate method, since by definition it blocks until the process is complete (or, as the docs put it: "Wait for process to terminate"). What you need to do is, read the documentation. Or heck, just read the answer that you already replied to: Johan Lundberg already told you that to investigate if the process has completed, you use the poll method. Commented Jun 16, 2013 at 18:20

4 Answers 4

1

If you use subprocess.Popen you wait for the returned handler with .wait(). You can investigate if the process has completed [and the exit code] with .poll() or interact with it (standard in/out) with .communicate().

The documentation is found here: http://docs.python.org/2/library/subprocess.html#module-subprocess

what have you tried?

Sign up to request clarification or add additional context in comments.

1 Comment

I've edited the question and put a simplified code of my program where you can see the on_fixme_button_clicked where I call run_script as thread. I have to look yet to the .wait() thing that you pointed
0

I am really not sure it will help, but I suggest you have a look at Pexpect module: http://pexpect.sourceforge.net/pexpect.html

As far, as I understand, it is a more sophisticated "launcher" for system calls.

Comments

0

Finally solved using GObject.timeout_add to update the state of the messages using a variable in the threaded class to know when the process is finished, and also using the .communicate() option suggested by @johan-lundberg

Comments

0

There are at least two issues:

  • target=self.run_script() is incorrect: it runs the function immediately. Drop (), to pass the method as an argument instead of calling it.
  • you call GUI method (on self.event_info.set_label) without protection from a background thread. The simplest way to fix it is to call set_label using idle_add():

    Gobject.idle_add(self.event_info.set_label, msg) # in a background thread
    

    It is safe to use idle_add because there is Gobject.init_threads() at the top of the module.

See also Threading in Gtk python.

I am using a separate thread to run my code, but the application (or the UI) hangs. (gtk2)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.