No hay dos sin tres

Nueva versión de GtkPSproc. Algo pasa con Cups, que se empeña en dar error al usar lpadmin para crear una nueva impresora si se especifica un fichero PPD. Afortunadamente GtkPSproc funciona bien sin él, por lo que lo he eliminado, de manera que reciba el PostScript original directamente.

Otra cosa que he descubierto es que el PPD para impresora PostScript genérica creado por FooMatic no pasa la prueba de cupstestppd. Parece que no le gusta que se especifique un parámetro por defecto con el nombre default.

Y por último, parece que python-fam no funciona bien con Python 2.5 en algunos equipos, así que he decidido sustituirlo por python-gamin, que se supone que es lo que se usa hoy en día.

De todas maneras, el código actual de GtkPSproc está demasiado sucio, así que estoy considerando reescribirlo en condiciones (en python, por supuesto), igual que he hecho con DeVeDe. De momento estoy rehaciendo la interfaz con Glade-3, porque no se que ocurre que si cargo el fichero XML de la vieja interfaz (creado con Glade-2) y modifico cualquier cosa, dejan de funcionar los eventos en el programa, como si no ejecutase el signal_autoconnect(). Supongo que será alguna incompatibilidad rara, no se.

Y ya que hablo de DeVeDe, hay noticia: en cuanto reciba los tres ficheros de traducciones que faltan lanzaré la versión 3.4, con soporte completo de sonido 5.1, aunque la verdadera novedad de esta versión es que, por fin, terminé de refactorizar todo el código y ahora ya se puede mirar para él sin sentir verguenza ajena; y lo que es más importante: ahora es extremadamente fácil añadir nuevas opciones de codificación. Espero que en las nuevas versiones se empiece a notar ésto.

¡Tenemos un problema!

A veces los fallos se quedan muy, pero que muy escondidos. Así, la última versión de GtkPSproc tenía uno garrafal que impedía por completo su uso. ¿Y como se me pudo colar? Pues porque sólo aparecía cuando no había un fichero de configuración en la cuenta.

Lo increible es que no me lo comunicaron hasta ayer, cuando esa versión tiene casi un año…

Selección multiple

En Trabajando con GtkTreeView en Python explicaba como utilizar el complejo pero versatil widget GtkTreeView. Sin embargo, faltaba un detalle importante: ¿como usar selección múltiple?

Lo primero es poner el selector del GtkTreeView en modo de selección múltiple:

treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)

Ahora ya podremos marcar más de una fila simultáneamente usando las teclas Mayúsculas y Control.

Leer los elementos marcados es tan sencillo como obtener primero el iterador y el arbol:

tree,iter = self.z88tree.get_selection().get_selected_rows()

Si el iterador es None, no hay ninguna fila seleccionada, pero si lo está, no tenemos más que recorrer los elementos del iterador para acceder a las filas. Con ellas, podemos usar el método get_value del arbol para obtener el valor de cada una.

En este ejemplo almacenamos en la lista ret el valor de la columna cero de cada fila seleccionada:

ret = []
for item in iter:
    ret.append(tree.get_value(tree.get_iter(item),0))

Fácil, fácil, para toda la familia.

Escribiendo con PyGTK como $DEITY manda

Hoy he lanzado una nueva versión de Z88Transfer. Los cambios observables por los usuarios son más bien pequeños: ahora muestra una barra de progreso durante la transferencia, permite transferir varios archivos de una sola tacada, comprueba más exhaustivamente si un archivo nuevo sobreescribe a uno viejo, y, sobre todo, simplifica muchísimo su uso en windows.

Sin embargo, por dentro los cambios son mucho más radicales, pues prácticamente he reescrito la aplicación. Las razones han sido tres:

  • Por un lado, varias partes del código estaban muy sucias y eran muy poco modulares.
  • Por otra, quería internacionalizar la aplicación, para poder traducirla a otros idiomas (aunque en esta versión todavía no hay traducciones).
  • Por último, quería organizar en condiciones el código de las ventanas poniendo los manejadores de eventos de cada una en su propia clase (haciéndolo así más modular mantenible), además de simplificarlo usando diálogos para los distintos mensajes que pueden aparecer, en lugar de ventanas modales.

La última razón es especialmente importante. En el caso de SuperShow, el código era ya bastante bueno pues es la última aplicación que he escrito hasta la fecha, pero en el caso de Z88Transfer el código era realmente sucio, pues fue la primera aplicación que hice con PyGTK. Sin embargo, gracias a que es una aplicación relativamente sencilla, el código aún era razonable (la transferencia de ficheros y la conversión de formatos van en sendas clases separadas, lo que simplificó la reescritura).

¿Y a qué viene ésto? Pues viene porque también estoy reescribiendo el código de DeVeDe. Cuando comencé a escribir con PyGTK no sabía como generar una única ventana ni como conectar los eventos de ésta con métodos de una clase, pues la documentación del módulo PyGlade era escasa, por no decir nula. El resultado fue que la única manera que tenía de programar era generando una instancia de absolutamente todas las ventanas disponibles en el archivo de Glade, y metiendo todas las funciones de eventos directamente en un único fichero, lo que dio lugar a un código… bueno, llamémoslo caótico (por ser benévolo conmigo mismo), y muy poco mantenible.

Ahora lo veo en retrospectiva y me parece increible que haya sido capaz de escalar el código original de DeVeDe hasta el nivel actual, aunque siendo yo el programador supongo que no tiene mucho mérito. El que sí que tiene mérito es, sin duda, Peter Gill, que lo revisó y modificó para que funcionase también en windows. Un código que fue escrito originalmente como una solución chapucera y rápida para hacerle con comodidad a mi madre una pequeña colección de DVDs con los programas de Arguiñano que bajaba de la mula.

En estos momentos ya he reescrito toda la parte de conversión de vídeos y de generación de la imagen de DVD usando una orientación a objetos bastante razonable, y posiblemente lance una versión sólo con esas modificaciones, pues el nuevo código debería ser mucho más resistente a fallos tontos (el propio Peter, cuando lo revisó para asegurarse de que funcionase en Windows, comentó que sería una tarea muchísimo más sencilla gracias a la nueva arquitectura). El siguiente paso será cambiar a una clase independiente todo el código de la ventana de propiedades de los vídeos, para permitir añadir con más facilidad nuevas opciones de codificación. Por último, meteré en su propia clase la ventana principal y pondré la ventana de selección de tipo de disco como un diálogo. Entonces podré dar por terminada la refactorización del código y seguir añadiendo cosas nuevas.

Esos pequeños placeres

Mi caja actual tiene un pequeño fallo de diseño: el conector frontal de USBs está muy cerca de la salida de auriculares, por lo que no puedo conectar a la vez los cascos y mi reproductor de MP3 para grabar cosas en él. Sin embargo, un poco más separada está la conexión de micrófono, por lo que se me ocurrió hacer un par de puentes para que el sonido salga por ésta, y así no tener problemas. Llevaba tiempo queriendo hacerlo, pero hasta hoy no saqué ganas.

¿Y qué tiene que ver ésto con el título? Bueno, la última vez que usé un soldador para hacer alguna chapucilla propia fue hace casi dos años, cuando amplié la memoria de mi Z88, y es también la primera vez que lo uso desde que me independicé. Volver a cogerlo, aunque fuese para algo tan sencillo, ha sido realmente agradable. No te das cuenta de lo mucho que echas de menos hacer algo hasta que lo vuelves a hacer y disfrutas. Llevo tanto tiempo concentrado en mi trabajo y en mis proyectos de programación que tengo la electrónica completamente abandonada.

Después de ésto quiero hacer algún proyectillo nuevo, pero antes tengo que comprar una mesa adecuada. ¿Por qué no hay un Ikea cerca cuando lo necesitas?

SuperShow

Finalmente he sacado la versión 2.0 de SuperShow. En ella he pulido la interfaz y he limpiado algo más el código. Entre las novedades está un código Flash muy mejorado, que ahora permite pausar la presentación, retroceder y avanzar, y reproducirla de nuevo cuando termina.

Otra importante novedad es que ahora permite grabar y volver a cargar las temporizaciones. Esto es especialmente interesante porque así es posible generar de nuevo una presentación con una versión posterior de SuperShow, aprovechando las ventajas que traiga (por ejemplo, nuevos controles de la presentación, corrección de bugs en el código Flash…).

Además, ahora la temporización es precisa, y no como en la versión anterior, en que las diapositivas cambiaban aproximadamente en el punto deseado.

Por último, ahora las presentaciones son compatibles con Gnash, el reproductor de Flash libre de GNU. Esto significa que, aún usando un formato propietario, mantengo la libertad de los usuarios. Más que en el código Flash, los cambios para ésto estuvieron en el propio Gnash, y precisamente tengo que reconocer que el equipo del proyecto se ha portado increiblemente bien: no solo resolvieron en un tiempo record los bugs más complicados que notifiqué, sino que admitieron todos los parches que envié para corregir otros bugs que sí eran lo suficientemente evidentes como para que pudiese arreglarlos yo. El resultado es que las presentaciones del nuevo SuperShow funcionarán casi al 100% en la próxima versión estable de Gnash.

Y digo «casi» porque, de momento, el avance, retroceso y pausa no funcionan correctamente debido a un bug en el método que activa un sonido. Sin embargo, están trabajando en ello y seguro que en breve estará implementado. Otro fallo es que, cuando simplemente se especifica un color de fondo en lugar de usar una primitiva o una imagen, Gnash lo pinta de negro (en las presentaciones hechas con Impress, si no se pone ingún fondo, debería ser blanco). Este bug es, por lo que comentan en el Bugzilla, bastante complejo, por lo que, como solución temporal, es recomendable utilizar un fondo hecho con un rectángulo de color que ocupe toda la diapositiva.

Version 4.9 de GAG

Hace un par de días tuve que sacar una nueva versión de GAG, la 4.9. La razón fue que, al cambiar toda la parte de configuración al principio del código, cometí un pequeño fallo: la posición de memoria en donde se almacena el tipo de teclado también quedó desplazada, pero el código de GAG seguía leyendo de la antigua. Para colmo, el instalador metía el valor del teclado en la posición antigua, con lo que el resultado era que, al arrancar GAG desde el instalador, el teclado funcionaba perfectamente, pero en cuanto se instalaba en disco duro y se reiniciaba, pasaba a usar un teclado aleatorio. En mi caso, por puro azar fue siempre QWERTY, por lo que no advertí el error; pero a varios usuarios les tocó el teclado ruso, con lo que se encontrarón con que, una vez instalado, no era posible modificar la configuración (sí podían arrancar los sistemas operativos previamente configurados, porque los números están en las mismas teclas en todas las lenguas).

Trabajando con PyCairo y GTK

En la nueva versión de DeVeDe (que espero sacar en un par de días) he tenido que trabajar con Cairo para poder generar menús para el disco. La información la saqué de este tutorial sobre PyCairo (en inglés), en donde viene mucha información interesante. Sin embargo, faltaba un detalle importante: como mostrar el dibujo hecho en cairo en una ventana GTK.

En efecto, esto era fundamental porque el usuario tiene que poder ver el resultado final del menú antes de generar el disco (para, por ejemplo, asegurarse de que los títulos no son demasiado largos), y para eso necesito pintarlo en una ventana y no sólo volcarlo a un PNG en disco. Después de mucho buscar y de hacer varias pruebas encontré por fin como hacerlo.

Lo primero es crear un widget GtkDrawingArea, en el que mostraremos el dibujo generado con Cairo, y crear un callback para el evento expose-event. Este evento se produce cada vez que hay que redibujar la ventana (bien porque estaba tapada por otra y la hemos destapado, bien porque estaba minimizada y la hemos maximizado…). En dicho callback será donde hagamos el pintado. Para ello, lo primero que tenemos que hacer es conseguir un contexto de Cairo para dicho widget. Así, si el widget se llama MiDibujo, y arbol es el objeto Glade con nuestras ventanas, haríamos:

# asignamos el callback a nuestra funcion de repintado
w=arbol.get_widget("MiDibujo")
w.connect("expose-event",repintado)

# y definimos el callback. Tiene dos argumentos
def repintado(dwidget,evento):
    cr=dwidget.window.cairo_create()
    [funciones y métodos de Cairo para pintar]

Y ya podremos usar ese contexto para pintar y trazar todo lo que queramos.

Sin embargo, si el dibujo es estático no tiene sentido que lo tracemos una y otra vez cada vez que se produzca un evento, porque desperdiciamos tiempo de proceso. Lo mejor es tenerlo pintado desde el principio y limitarnos a copiar el bitmapa final en el callback de repintado. Para ello sólo tenemos que crear primero una superficie de Cairo y obtener un contexto para ella:

sf=cairo.ImageSurface(cairo.FORMAT_ARGB32,ResX,Resy)
cr1=cairo.Context(sf)

Ahora pintaremos nuestro dibujo en dicha superficie con ese contexto, y una vez que hayamos terminado, almacenaremos el objeto superficie de alguna manera que nos permita acceder a él desde nuestro callback (una variable global es la solución más rápida y menos elegante). Allí sólo tenemos que usar los métodos set_source_surface y paint para transferir la superficie a nuestro widget GtkDrawingArea, ahorrando mucho tiempo de proceso.

def repintado(dwidget,evento):
    cr=dwidget.window.cairo_create()
    # sf es la que pintamos en el paso anterior
    cr.set_source_surface(sf)

    # transferimos la superficie con el dibujo al Widget
    cr.paint()

Este truco nos permite también pegar cualquier imagen en la superficie en la que estamos trabajando, e incluso redimensionarla en el proceso. Un ejemplo: queremos crear una imagen de tamaño X,Y, usando como fondo un PNG de tamaño cualquiera, pero de manera que éste se expanda o encoja para ocupar exactamente la superficie de nuestra imagen final. Sólo tendríamos que hacer:

sf_base= cairo.ImageSurface.create_from_png("imagen.png")
sf_final=cairo.ImageSurface(cairo.FORMAT_ARGB32,X,Y)

# creamos un contexto para pintar
cr_final=cairo.Context(sf_final)

# cogemos el ancho y alto de la imagen.
# Importante: en coma flotante
xbase=float(sf_base.get_width())
ybase=float(sf_base.get_height())

# aplicamos el escalado para que la imagen de fondo
# pase a tener el mismo tamaño que la imagen final
cr_final.scale(float(X)/xbase,float(Y)/ybase)

# y estampamos la imagen de origen en la superficie final
cr_final.set_source_surface(sf_base)
cr_final.paint()

# podemos usar el método identity para
#restaurar la escala a 1:1
cr.identity_matrix()

A vueltas con Mplayer

Parece que no ha bastando con usar la versión del SVN de Mplayer/Mencoder para solucionar los problemas: al generar DVDs en formato PAL, en algunos casos lo hace en blanco y negro, amén de otros pequeños problemas.

Ante ésto he preferido curarme en salud y he retrocedido a la versión 0.99pre8; para ser más exactos, la que venía en Ubuntu Edgy. Dicha versión funcionó siempre razonablemente bien.

Para evitar más problemas de dependencias, he preferido empaquetar sólo los ejecutables en un tar.bz2, e incluir un script que sustituya los que ya haya en el sistema por los «nuevos». Esto lo hace, además, compatible con cualquier distribución, y no sólo con Ubuntu.

Actualización: por petición popular he añadido otro paquete con Mplayer/Mencoder para 64 bits.

Paquetes

Parafraseando a Germán Poo, la llegada de la nueva Ubuntu Feisty no es para montar una fiesta, precisamente. La razón es que la versión que trae de Mencoder (la 1.0-rc1, actual versión estable) tiene un fallo al codificar el sonido, con lo que DeVeDe es completamente inútil.

El bug lleva tiempo coleando, y la única solución en estos momentos parece ser utilizar la versión en desarrollo, sacada del SVN (en mi web, por despiste y costumbre, hablo de CVS. ¡Perdón, perdón!). Como una cosa es hacer un sudo ./install, que cualquiera lo hace, y otra muy distinta bajarse fuentes de un SVN y descubrir qué bibliotecas son necesarias para compilar un programa tan complejo como MPlayer/Mencoder, he decidido hacer yo mismo un paquete .deb con la versión del SVN del 14 de abril de 2007, compilada sobre Feisty, para que los usuarios no queden atados.