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

CC BY-SA 4.0 Trabajando con PyCairo y GTK por A cuadros está licenciado bajo una Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional.

6 comentarios en “Trabajando con PyCairo y GTK

  1. Me gustaria saber como se hace para que luego de ejecutar el signal handler de la seleccion de items, el treeview continue mostrandolos como seleccionados.
    En mi caso sucede que una vez que pico el boton derecho la seleccion se rompe.

    Saludos
    JC

  2. Que tal:
    Primero dejame felicitarte por tan excelente programa como lo es el DeVeDe.
    Te pido disculpas de antemano porque creo que este no es el sitio indicado para hacer esta solicitud, sin embargo aqui va. Soy una nulidad en programacion, a pesar de haber hecho algunos esfuerzos en ese sentido siempre quedo en blanco. Seria posible agregar la opcion de poder escoger el color del subtitulo?. Por ejemplo en vez de que queden simplemente en color blanco, digamos que quedaran en color amarillo?

    Muchos Saludos

    Roberto de Diego

  3. Entiendo, sabes algo? hay veces en que el subtitulo esta en formato .sub y SPUMUX, como decimos por aca, «se tildea» y lanza un error…no se si habras tenido oportunidad de ver algo como eso…

  4. Puede ser porque haya alguna frase demasiado grande y SPUMUX no es capaz de meterla en una sola línea… Una solución es reducir el tamaño de los subtítulos… a ver si tengo un rato y lo añado.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *