Skip to content

Instantly share code, notes, and snippets.

@fabrixxm
Last active May 17, 2023 07:30
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save fabrixxm/8deb791ad0930fa209be to your computer and use it in GitHub Desktop.
Save fabrixxm/8deb791ad0930fa209be to your computer and use it in GitHub Desktop.
Run external process asynchronously with Python, GLib. Get stdout and stderr via signals.
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#https://developer.gnome.org/pygobject/2.28/
#http://www.pygtk.org/articles/subclassing-gobject/sub-classing-gobject-in-python.htm#d0e570
from gi.repository import GObject
from gi.repository import GLib
class GAsyncSpawn(GObject.GObject):
""" GObject class to wrap GLib.spawn_async().
Use:
s = GAsyncSpawn()
s.connect('process-done', mycallback)
s.run(command)
#command: list of strings
"""
__gsignals__ = {
'process-done' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_INT, )),
'stdout-data' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING, )),
'stderr-data' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
(GObject.TYPE_STRING, )),
}
def __init__(self):
GObject.GObject.__init__(self)
def run(self, cmd):
r = GLib.spawn_async(cmd,flags=GLib.SPAWN_DO_NOT_REAP_CHILD, standard_output=True, standard_error=True)
self.pid, idin, idout, iderr = r
fout = os.fdopen(idout, "r")
ferr = os.fdopen(iderr, "r")
GLib.child_watch_add(self.pid,self._on_done)
GLib.io_add_watch(fout, GLib.IO_IN, self._on_stdout)
GLib.io_add_watch(ferr, GLib.IO_IN, self._on_stderr)
return self.pid
def _on_done(self, pid, retval, *argv):
self.emit("process-done", retval)
def _emit_std(self, name, value):
self.emit(name+"-data", value)
def _on_stdout(self, fobj, cond):
self._emit_std("stdout", fobj.readline())
return True
def _on_stderr(self, fobj, cond):
self._emit_std("stderr", fobj.readline())
return True
"""
Example code
"""
if __name__=="__main__":
import os
from gi.repository import Gtk
class SpawnExample(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Example")
box = Gtk.VBox()
self.add(box)
self.command = ['/usr/bin/find', os.environ['HOME'], '-name','*.mp3']
self.button = Gtk.Button("Run '%s'" % ' '.join(self.command))
self.button.connect("clicked", self.on_button_clicked)
box.pack_start(self.button, False, True, 6)
self.spinner = Gtk.Spinner()
box.pack_start(self.spinner, False, True, 6)
self.spawn = GAsyncSpawn()
self.spawn.connect("process-done", self.on_process_done)
self.spawn.connect("stdout-data", self.on_stdout_data)
self.spawn.connect("stderr-data", self.on_stderr_data)
def on_button_clicked(self, sender):
self.spinner.start()
self.button.set_sensitive(False)
pid = self.spawn.run(self.command)
print "Started as process #",pid
def on_process_done(self, sender, retval):
self.spinner.stop()
self.button.set_sensitive(True)
print "Done. exit code:", retval
def on_stdout_data(self, sender, line):
print "[STDOUT]", line.strip("\n")
def on_stderr_data(self, sender, line):
print "[STDERR]", line.strip("\n")
win = SpawnExample()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment