| FAQ Index - Search - Recent Changes - Everything - Add entry |
14.23. How do I send the output of an external process to a gtk.TextView without freezing the GUI?
This example shows how to do it the "threaded way", as it seems quite straightforward and portable through platforms without additional tweaking [1].
- -------------------------------------
#!/usr/bin/env python
"""Show a shell command's output in a gtk.TextView without freezing the UI"""
import os, threading, locale
import gobject
import gtk
gobject.threads_init()
gtk.gdk.threads_init()
encoding = locale.getpreferredencoding()
utf8conv = lambda x : unicode(x, encoding).encode('utf8')
def on_button_clicked(button, view, buffer, command):
thr = threading.Thread(target= read_output, args=(view, buffer, command))
thr.start()
def read_output(view, buffer, command):
stdin, stdouterr = os.popen4(command)
while 1:
line = stdouterr.readline()
if not line:
break
gtk.gdk.threads_enter()
iter = buffer.get_end_iter()
buffer.place_cursor(iter)
buffer.insert(iter, utf8conv(line))
view.scroll_to_mark(buffer.get_insert(), 0.1)
gtk.gdk.threads_leave()
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
textview = gtk.TextView()
textbuffer = textview.get_buffer()
sw.add(textview)
win = gtk.Window()
win.resize(300,500)
win.connect('delete-event', gtk.main_quit)
button = gtk.Button(u"Press me!")
command = 'ls -R %s' % (os.getcwd(),)
button.connect("clicked", on_button_clicked, textview, textbuffer, command)
vbox = gtk.VBox()
vbox.pack_start(button, False)
vbox.pack_start(sw)
win.add(vbox)
win.show_all()
gtk.main()
- ---------------------------------------------
Note that this code is dangerous: if the widget is destroyed while the thread is still running, nasty things may happen, from nothing visible to segfault.
[1] Nobody really knows if this works on windows.
- ---
Dangerous code is wonderful, but how would you do this safely? Is there locking involved? Where?
When multiple threads are writing to the same text buffer, the program has a tendency to fail an assert and segfault on the buffer.insert line. If you explicitly silence the assert error, the interpreter tends to crash. This probably isn't a result of bad interpreter code, but just really nasty thread handling.
Moral of the story: Don't let multiple threads write to the same text-buffer. Create a new one for every thread.
