Last modification Sun Jan 9 12:35:57 2005
Eric Herrera "El Crac - crackito" <eric_at_tuxteno_dot_com>

Cairo: La máquina de dibujo de Gnome 3

Licencia

Este tutorial está cubierto por FDL. Los programas listados aquí estan cubiertos por LGPL.

Referencia Rápida

Index

Introducción

Freedesktop, también conocido como fdo, es un proyecto software libre iniciado por de uno de los desarrolladores de GNOME, Havoc Pennington, para la estandarización, integración e interoperabilidad de los distintos escritorios.

Freedesktop surgió principalmente como un proyecto entorno al sistema X para la estandarización de los escritorios de Linux con el fin de conseguir que tecnologías existentes estén más integradas, de forma que no haya una total diversidad y separación entre proyectos.

Para ello Freedesktop lo que pretende conseguir es una base de software común para todos los escritorios, englobando tecnologías como las de Mozilla, distintas herramientas gŕaficas como Qt y gtk+, VCL, wine, GNOME, KDE y X Window System.
De este concepto de estandarización nace Cairo

Cairo Graphics

Cairo, intoducida al mundo del Software Libre por Keith Packard, es una biblioteca de gráficos vectoriales en 2D, diseñada para proporcionar imagenes con alta calidad incluídos en el sistema X, OpenGL, buffers de imagen en la memoria y archivos de imagen.

Cairo se diseñó para producir una salida idéntica en todos los dispositivos de salida, mientras que aprovecha la aceleración del hardware cuando este disponible disponible.
Lo que pretende es ofrecer un renderizado independiente de la plataforma, rápido y flexible.

Diseño de la interface gráfica

Antes que nada debemos de saber cómo se va a crear la interface gráfica, y está la realizaremos con el diseñador de interfaces de Gnome Glade. Primero lanzamos nuestro diseñador de interfaces

[eric@localhost]glade-2

Lo primero que veremos es la ventana principal de Glade, en ella crearemos un nuevo proyecto ya sea GTK o Gnome.

glade.png

glade.png

Posteriormente empezaremos a diseñar la interface, glade nos mostrará diferentes barras, entre ellas la paleta de widgets que es la siguiente.

palette.png

palette.png

Esta paleta nos muestra los diferentes widgets y diálogos de Gtk y Gnome. Seleccionaremos el widget de ventana para empezar a crear nuestra interface, este elemento nos crea un nueva ventana en la cuál podremos seguir insertando widgets.

window1.png

window1.png

Ahora, para continuar insertando elementos de una manera ordenada y limpia seleccionaremos el widget de tabla y la insertaremos, le asignaremos 2 filas.

table.png

table.png

En la primera fila insertaremos un drawingarea, está sera nuestra area para dibujar, y en la siguiente fila un botón, le podemos asignar un ícono y nombre en la caja de propiedades que veremos más adelante.

window2.png

window2.png

Hasta este momento tenemos creada la interface, pero a cada widgets que insertamos le vamos a añadir señales, estas señales nos servirán para poder manejar nuestros gidgets directamente con python. Podemos seleccionar los widgets desde el árbol de widgets.

tree.png

tree.png

Proseguiremos a añadir las señales, tenemos una caja que nos miestra las propiedades de los widgets, en ésta podemos modificar sus atributos, así como tamaño, nombre, señales entre otras, nosotros nos enfocaremos en señales.

properties.png

properties.png

Primero seleccionamos en nuestro árbol de widgets la ventana, a la cuál le agregaremos una señal de 'destroy' y en el manejador seleccionaremos 'gtk_main_quit', y presionaremos añadir, esto servirá para que cuando cerremos nuestra ventana esta se destruya. Después seleccionamos el widget de drawingarea y seleccionaremos la señal de 'expose_event' y en el manejador podemos escribir 'on_expose' y lo añadimos, por último resta el button, seleccionaremos la señal de 'clicked' y el manejador añadiremos 'gtk_main_quit' esto para que al presionarlo la venta se cierre. Después de esto ya tendremos creada nuestra interface base para todos nuestros programas.

signals.png

signals.png

Ejemplos de Cairo

En este ejemplo veremos la potencia de Cairo para dibujar curvas y líneas con python.

Cairo7GladeApp.png

Cairo7GladeApp.png

Cairo7GladeApp.glade

Cairo7GladeApp.py

#!/usr/bin/env python
# -*- coding:UTF-8 -*-

from SimpleGladeApp import SimpleGladeApp
import gtk
import cairo
import cairo.gtk

class Cairo7GladeApp(SimpleGladeApp):
        def on_expose_event(self, area, event):
                self.ctx = cairo.Context()
                cairo.gtk.set_target_drawable(self.ctx, self.drawingarea1.window)
                width, height = self.drawingarea1.window.get_size()
                self.ctx.scale(width/100.0, height/100.0)

                self.ctx.set_rgb_color(1, 1, 1)
                self.ctx.rectangle(0, 0, width, height)
                self.ctx.fill()

                self.ctx.translate(-0.5, 0)
                self.draw_curve()
                self.draw_lines()

        def draw_curve(self):
                self.ctx.move_to(10, 50)
                self.ctx.curve_to(40, 90, 60, 10, 90, 50)
                self.ctx.set_rgb_color(0,0,0)
                self.ctx.set_line_width(3)
                self.ctx.stroke()

        def draw_lines(self):
                self.ctx.move_to(10, 50)
                self.ctx.line_to(40, 90)
                self.ctx.move_to(60, 10)
                self.ctx.line_to(90, 50)
                self.ctx.set_alpha(0.6)
                self.ctx.set_rgb_color(1, 0.2, 0.2)
                self.ctx.stroke()

app = Cairo7GladeApp('Cairo7GladeApp.glade')
app.run()

Para correr utilizar las bibliotecas de cairo en python debes instalar el paquete de python-cairo. Ahora le daremos permisos de ejecución al archivo .py de python

[crac@localhost Cairo7GladeApp]$chmod u+x Cairo7GladeApp.py
[crac@localhost Cairo7GladeApp]./Cairo7GladeApp.py

Polígonos

Ahora veremos como realizar un polígono con un texto animado.

CairoGladeApp.png

CairoGladeApp.png

CairoGladeApp.glade

CairoGladeApp.py

#!/usr/bin/env python
# -*- coding UTF-8 -*-

from SimpleGladeApp import SimpleGladeApp
import gtk
import cairo
import cairo.gtk
import time
#import math

class CairoGladeApp(SimpleGladeApp):
        def new(self):
                self.label = "STOP!"
                self.count = 0
                gtk.timeout_add(500, self.on_timeout)

        def on_timeout(self):
                self.count += 1
                if self.count > len(self.label):
                        self.count = 0
                self.window1.queue_draw()
                return True

        def on_expose_event(self, area, event):
                self.ctx = cairo.Context()
                cairo.gtk.set_target_drawable(self.ctx, self.drawingarea1.window)
                width, height = self.drawingarea1.window.get_size()
                size = min(width, height)
                self.ctx.scale(width, height)

                self.ctx.set_alpha(0.75)
                self.ctx.set_rgb_color(1, 1, 1)
                self.ctx.rectangle(0, 0, 1, 1)
                self.ctx.fill()

                self.draw_polygon()
                self.draw_text()
                self.draw_tube()

        def draw_polygon(self):
                self.ctx.translate(0.1875, -0.15)
                self.ctx.move_to(0.1875, 0.1875)
                self.ctx.rel_line_to(0.25, 0)
                self.ctx.rel_line_to(0.1875, 0.1875)
                self.ctx.rel_line_to(0, 0.25)
                self.ctx.rel_line_to(-0.1875, 0.1875)
                self.ctx.rel_line_to(-0.25, 0)
                self.ctx.rel_line_to(-0.1875, -0.1875)
                self.ctx.rel_line_to(0, -0.25)
                self.ctx.close_path()

                self.ctx.save()
                self.ctx.set_rgb_color(0, 0, 0)
                self.ctx.set_line_width(0.005)
                self.ctx.set_line_join(cairo.LINE_JOIN_BEVEL)
                self.ctx.stroke()
                self.ctx.restore()
                self.ctx.set_rgb_color(1, 0, 0)
                self.ctx.fill()

        def draw_text(self):
                self.ctx.save()
                self.ctx.move_to(0.05, 0.55)
                self.ctx.set_rgb_color(0, 0, 0)
                self.ctx.select_font("babelfish")
                self.ctx.scale_font(0.27)
                self.ctx.show_text(self.label[0:self.count])
                self.ctx.stroke()
                self.ctx.restore()

        def draw_tube(self):
                self.ctx.move_to(0.25, 0.815)
                self.ctx.rel_line_to(0, 0.3)
                self.ctx.rel_line_to(0.125, 0)
                self.ctx.rel_line_to(0, -0.3)
                self.ctx.set_rgb_color(0.2, 0.1, 0.3)
                self.ctx.close_path()
                self.ctx.save()
                self.ctx.set_rgb_color(0, 0, 0)
                self.ctx.set_line_width(0.005)
                self.ctx.stroke()
                self.ctx.restore()
                self.ctx.fill()

app = CairoGladeApp("CairoGladeApp.glade")
app.run()

Lineas y Alfas

Este ejemplo nos muestra como utilizar los segmentos de línea formnado un cubo, así como utilizar translucencias.

Cairo4GladeApp.png

Cairo4GladeApp.png

cairo4gladeapp.glade

Cairo4GladeApp.py

#!/usr/bin/env python
# -*- coding UTF-8 -*-

from SimpleGladeApp import SimpleGladeApp
import gtk
import cairo
import cairo.gtk

class Cairo4GladeApp(SimpleGladeApp):
        def new(self):
                self.elapsed = 95
                self.count = 0
                self.on_timeout()

        def on_timeout(self):
                gtk.timeout_add(self.elapsed, self.on_timeout)
                self.count += 0.01
                if self.count > 1:
                        self.count = 0
                self.drawingarea1.queue_draw()

        def on_expose_event(self, area, event):
                self.ctx = cairo.Context()
                cairo.gtk.set_target_drawable(self.ctx, self.drawingarea1.window)
                width, height = self.drawingarea1.window.get_size()
                self.ctx.scale(width/100.0, height/100.0)

                self.ctx.set_rgb_color(0,0,0)
                self.ctx.rectangle(0, 0, width, height)
                self.ctx.fill()

                self.draw_rectangle1()
                self.draw_rectangle4()
                self.draw_rectangle2()
                self.draw_rectangle3()

        def draw_rectangle1(self):
                self.ctx.set_line_width(0.3)
                self.ctx.set_rgb_color(1, 1, 1)
                self.ctx.rectangle(27, 27, 30, 30)
                self.ctx.save()
                self.ctx.set_alpha(self.count)
                self.ctx.set_rgb_color(0.4, 0.7, 0.9)
                self.ctx.fill()
                self.ctx.restore()
                self.ctx.stroke()

        def draw_rectangle2(self):
                self.ctx.rectangle(39, 39, 30, 30)
                self.ctx.save()
                self.ctx.set_alpha(self.count)
                self.ctx.set_rgb_color(0.3, 0.1, 0.2)
                self.ctx.fill()
                self.ctx.restore()
                self.ctx.stroke()

        def draw_rectangle3(self):
                self.ctx.set_line_width(0.2)
                self.ctx.move_to(27, 27)
                self.ctx.rel_line_to(30, 0)
                self.ctx.rel_line_to(12, 12)
                self.ctx.rel_line_to(-30, 0)
                self.ctx.close_path()
                self.ctx.save()
                self.ctx.set_alpha(self.count)
                self.ctx.set_rgb_color(0.8, 0.4, 0.5)
                self.ctx.fill()
                self.ctx.restore()
                self.ctx.stroke()

        def draw_rectangle4(self):
                self.ctx.move_to(27, 57)
                self.ctx.rel_line_to(30, 0)
                self.ctx.rel_line_to(12, 12)
                self.ctx.rel_line_to(-30, 0)
                self.ctx.close_path()
                self.ctx.save()
                self.ctx.set_alpha(self.count)
                self.ctx.set_rgb_color(0.2, 0.4, 0.2)
                self.ctx.fill()
                self.ctx.restore()
                self.ctx.stroke()

app = Cairo4GladeApp('cairo4gladeapp.glade')
app.run()

References