gentoo linux, java, software libre y otras hierbas
Oct, 04 2008 - 10:10 am

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()

  1. Lo primero (línea 2) es importar el paquete threading, que nos permitirá usar threads
  2. 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
  3. 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), y gtk.gdk.threads_leave() después de las mismas (línea 23).
Asumo que, si este post es útil para ti es porque ya tienes experiencia programando en PyGtk, por lo que pasaré de explicar los detalles relacionados con la creación de widgets, eventos, etc.

El resultado del programa es el siguiente:

pygtk thread

Descargar código fuente del ejemplo

Etiquetas: gtk, hilos, pygtk, python, threads

3 Comentarios | deja el tuyo

Un enlace entrante

2 Comentarios en “Threads en Python y PyGtk – evitar que se congelen las aplicaciones”

  1. Joaquín Sargiotto dice:

    Excelente ejemplo! conciso y preciso, justo lo que buscaba.

  2. markuz dice:

    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.

¡Déjanos tu comentario!