[pygtk] Show command output (new IEs4Linux GUI)

Internet Explorer Linux ies4linux at gmail.com
Fri Dec 8 14:38:40 WST 2006


Hey, thank you guys!
I decided to do it with threads. See how it looks now:
tatanka.com.br/ies4linux/news

On 12/1/06, Edward Catmur <ed at catmur.co.uk> wrote:
>
> On Thu, 2006-11-30 at 04:14 -0200, Internet Explorer Linux wrote:
> > I'm working on a pygtk user interface to my script. My GUI configures
> > some things and run an executable (IEs4linux installer, written in
> > bash). I need to open some kind of window to show the installer
> > output.
> >
> > I tried with TextView, but I don't know how to make it non-blocking. I
> > tried with Threads, but it waits until installer finishes to show the
> > output on TextView. I searched on Google and got some pages talking
> > about this (even with "solutions"), but I could not make it work.
>
> You need to tie the fd (from your child's stdout) into the GLib
> mainloop.  The way to do this is to make it nonblocking and add a watch
> on it; your watch callback then adds the data that appears on the fd to
> the textview.
>
> Obviously threads would work as well, but if you can write your
> application single-threaded it'll save a load of heartache debugging
> races when you get the reentrancy and locking wrong.
>
> Here's a fairly comprehensive example; I put it together to demonstrate
> watching the output of a DVD burning script, but it's obviously
> applicable to your situation.
>
> Ed
>
> #!/usr/bin/env python
>
> import gobject, gtk, sys, os, fcntl
>
> def get_stock_icons(widget, id):
>         set =3D widget.style.lookup_icon_set(id)
>         return map(lambda x: set.render_icon(widget.style,
> gtk.TEXT_DIR_NONE,
>                 gtk.STATE_NORMAL, x, widget, None), set.get_sizes())
>
> class LogWindow:
>         def input_callback(self, source, condition):
>                 if condition & (gobject.IO_HUP):
>                         self.eof()
>                         return False
>                 try:
>                         while True:
>                                 line =3D source.readline()
>                                 print line.__repr__()
>                                 if line =3D=3D '':
>                                         self.eof()
>                                         return False
>                                 self.append_text(line)
>                 except IOError:
>                         pass
>                 return True
>
>         def eof(self):
>                 self.ok.set_sensitive(True)
>                 self.cancel.set_sensitive(False)
>                 self.warn.hide()
>
>         def append_text(self, text):
>                 end_iter =3D self.buffer.get_end_iter()
>                 endmark =3D self.buffer.create_mark(None, end_iter)
>                 self.textview.move_mark_onscreen(endmark)
>                 at_end =3D self.buffer.get_iter_at_mark
> (endmark).equal(end_iter)
>                 self.buffer.insert(end_iter, text)
>                 if at_end:
>                         endmark =3D self.buffer.create_mark(None, end_ite=
r)
>                         self.textview.scroll_mark_onscreen(endmark)
>
>         def delete_event(self, widget, data =3D None):
>                 self.warn.show_all()
>                 return True
>
>         def destroy(self, widget, data =3D None):
>                 gtk.main_quit()
>
>         def expanded(self, widget, param_spec, data =3D None):
>                 if widget.get_expanded():
>                         self.window.set_resizable(True)
>                 else:
>                         self.window.set_resizable(False)
>
>         def response(self, widget, response_id, data =3D None):
>                 if response_id =3D=3D gtk.RESPONSE_OK:
>                         self.destroy(widget)
>                 elif response_id =3D=3D gtk.RESPONSE_CANCEL:
>                         self.delete_event(widget)
>
>         def __init__(self, source):
>                 # make source non-blocking
>                 fd =3D source.fileno()
>                 fcntl.fcntl(fd, fcntl.F_SETFL,
>                                 fcntl.fcntl(fd, fcntl.F_GETFL) |
> os.O_NONBLOCK)
>                 gobject.io_add_watch(source,
>                                 gobject.IO_IN | gobject.IO_PRI |
> gobject.IO_HUP,
>                                 self.input_callback)
>
>                 window =3D gtk.MessageDialog(buttons =3D gtk.BUTTONS_OK_C=
ANCEL
> )
>                 for x in window.action_area.get_children():
>                         if x.get_use_stock():
>                                 if x.get_label() =3D=3D gtk.STOCK_OK:
>                                         self.ok =3D x
>                                 elif x.get_label() =3D=3D gtk.STOCK_CANCE=
L:
>                                         self.cancel =3D x
>                 self.ok.set_sensitive(False)
>                 window.set_markup(
> """<b>The files are being burnt to DVD.</b>
>
> Please wait while the process completes.""")
>                 window.set_title("Burning DVD")
>                 gtk.window_set_default_icon_list(
>                                 *get_stock_icons(window, gtk.STOCK_CDROM))
>                 window.connect("delete_event", self.delete_event)
>                 window.connect("destroy", self.destroy)
>                 window.connect("response", self.response)
>                 self.window =3D window
>
>                 warn =3D gtk.MessageDialog(parent =3D window,
>                                 flags =3D gtk.DIALOG_MODAL,
>                                 type =3D gtk.MESSAGE_WARNING,
>                                 buttons =3D gtk.BUTTONS_CANCEL)
>                 warn.add_button("Abort DVD", gtk.RESPONSE_CLOSE)
>                 warn.set_markup("""<b>Are you sure you want to abort
> burning the
> DVD?</b>
>
> If the burning process has already started, the DVD will be ruined.""")
>                 def warn_response(widget, response_id, data =3D None):
>                         if response_id =3D=3D gtk.RESPONSE_CANCEL:
>                                 warn.hide()
>                         elif response_id =3D=3D gtk.RESPONSE_CLOSE:
>                                 self.returnError =3D 1
>                                 self.destroy(widget, True)
>                 warn.connect("response", warn_response)
>                 self.warn =3D warn
>
>                 expander =3D gtk.Expander("_Display detailed output")
>                 expander.set_use_underline(True)
>                 expander.connect("notify::expanded", self.expanded)
>                 window.vbox.pack_start(expander, True, True, 0)
>
>                 textview =3D gtk.TextView()
>                 textview.set_editable(False)
>                 self.textview =3D textview
>                 self.buffer =3D self.textview.get_buffer()
>                 scroll =3D gtk.ScrolledWindow()
>                 scroll.add(self.textview)
>                 scroll.set_size_request(0, 100)
>                 expander.add(scroll)
>
>                 window.show_all()
>
>         def main(self):
>                 self.returnError =3D 0
>                 gtk.main()
>                 return self.returnError
>
> if __name__ =3D=3D "__main__":
>         sys.exit(LogWindow(sys.stdin).main())
>
> _______________________________________________
> pygtk mailing list   pygtk at daa.com.au
> http://www.daa.com.au/mailman/listinfo/pygtk
> Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.daa.com.au/pipermail/pygtk/attachments/20061208/bab03e24/at=
tachment.htm


More information about the pygtk mailing list