Threads en Python y PyGtk – evitar que se congelen las aplicaciones
Uno de los típicos problemas que se tienen al programar en PyGtk es que si necestamos ejecutar un comando externo, nuestra aplicación se bloquea hasta que el proceso termine. La solución a esto es usar threads, pero NO “de la manera normal“.
La cosa es que, cuando estamos desarrollando software en PyGtk, usar threads de la forma convencional tiene problemas: algunas veces, los widgets como botones que ejecutan threads no trabajan adecuadamente, o simplemente la aplicación se congela como si no estuvieramos usando hilos
La solución es usar los métodos gtk.gdk.threads_init(), gtk.gdk.threads_enter() y gtk.gdk.threads_leave(). Vamos con un ejemplo, y luego de ello paso a explicar las particualiaridades del programa:
#!/usr/bin/env python
import os, threading, locale, gtk
gtk.gdk.threads_init()
codificacion = locale.getpreferredencoding()
utf8conv = lambda x : unicode(x, codificacion).encode('utf8')
def clic_boton(boton, area_texto, buffer, comando):
hilo = threading.Thread(target=leer_salida, args=(area_texto, buffer, comando))
hilo.start()
def leer_salida(area_texto, buffer, comando):
stdin, stdouterr = os.popen4(comando)
while 1:
linea = stdouterr.readline()
if not linea:
break
gtk.gdk.threads_enter()
iter = buffer.get_end_iter()
buffer.place_cursor(iter)
buffer.insert(iter, utf8conv(linea))
area_texto.scroll_to_mark(buffer.get_insert(), 0.1)
gtk.gdk.threads_leave()
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
area_texto = gtk.TextView()
buffer_texto = area_texto.get_buffer()
sw.add(area_texto)
ventana = gtk.Window()
ventana.resize(300,300)
ventana.set_title("Hilos en PyGtk")
ventana.connect('delete-event', gtk.main_quit)
boton = gtk.Button("Haz clic!")
comando = 'ls -R $HOME'
boton.connect("clicked", clic_boton, area_texto, buffer_texto, comando)
vbox = gtk.VBox()
vbox.pack_start(boton, False)
vbox.pack_start(sw)
ventana.add(vbox)
ventana.show_all()
gtk.main()
- Lo primero (línea 2) es importar el paquete
threading, que nos permitirá usar threads - Ahora, es necesario invocar la función
gtk.gdk.threads_init()al inicio del programa (línea 3), lo que le indica a la aplicación que se van a usar hilos en la interfaz gráfica - En tercer lugar, debemos identificar las líneas de nuestro programa que pueden congelar la aplicación, que por lo general son llamadas a comandos externos. Una vez indentificadas basta con invocar la función
gtk.gdk.threads_enter()antes de dichas líneas (línea 18), ygtk.gdk.threads_leave()después de las mismas (línea 23).
El resultado del programa es el siguiente:

Descargar código fuente del ejemplo
3 Comentarios | deja el tuyo



Excelente ejemplo! conciso y preciso, justo lo que buscaba.
Buen ejemplo, el problema de usar gtk.gdk.threads_init en lugar de gobject.threads_init es que gtk.gdk.threads_init no funciona correctamente en Win32, por lo que si usas threading en win32 lo mejor es usar gobject.threads_init para tener tu hilo trabajando y emitir señales para provocar que por otro lado con gobject.idle_add ejecute la funcion en tu hilo principal. Puedes leer un poco mas en este post:
http://www.islascruz.org/html/index.php?Blog/SingleView/id/PyGtk-and-Threads
Si usas objetos tipo archivo (sockets por ejemplo) puedes usar gobject.io_add_watch : http://www.islascruz.org/html/index.php?Blog/SingleView/id/Sockets-(and-some-other-files)-and-PyGTK-without-threads.