Mostrando entradas con la etiqueta Cairo. Mostrar todas las entradas
Mostrando entradas con la etiqueta Cairo. Mostrar todas las entradas

Cairo, PyWebKit y PyGTK: Semana de Pruebas

jueves, octubre 22, 2009

Esta ha sido una semana de pruebas, pruebas y más pruebas. Estoy trabajando en eso de mejorar las interfaces en PyGTK y pretendo apoyarme en Cairo y WebKit. He investigado un poco de ambos; los resultados han sido alentadores.

Primera prueba de la semana: Cairo

Cairo es una librería que permite dibujar sobre un widget (canvas o lienzo). Y cuando digo dibujar, me refiero a dibujar. Trazar líneas, rectángulos, arcos y cosas por el estilo.

Lo primero que se me ocurrió hacer para probar Cairo fue un medidor. Para esto solo me haría falta un slider vertical y el canvas para dibujar. La idea es que el medidor se llene o se vacíe según el deslizamiento de la barra.

El código del medidor quedó así (se explica con los comentarios):
#!/usr/bin/python

# Ejemplo de widget con Cairo
#
# Author: Wil Alvarez (aka Satanas)
# Oct 19, 2009

import gtk
import cairo

# Creamos una clase que herede de gtk.DrawingArea para usarla como canvas
class Cpu(gtk.DrawingArea):
    def __init__(self, parent):
        self.par = parent
        gtk.DrawingArea.__init__(self)
        # Nos conectamos al evento expose, pues allí es donde ocurre toda 
        # la diversión
        self.connect('expose-event', self.expose)
        self.set_size_request(130, 200)
    
    # Este evento se ejecuta cada vez que la aplicación necesita redibujarse
    # o cuando cambiamos un valor y mandamos a redibujarla. Aquí se pintará
    # y se le dará forma al widget
    def expose(self, widget, event):
        # Aquí obtenemos el contexto de cairo
        cr = widget.window.cairo_create()
        cr.set_line_width(0.8)
        
        # Definimos un rectángulo para limitar el proceso de dibujado y así
        # optimizar la operación
        cr.rectangle(event.area.x, event.area.y, 
            event.area.width, event.area.height)
        cr.clip()
        
        cr.rectangle(0,0,130,200)
        cr.set_source_rgb(0, 0, 0)  # Establecemos el color de la brocha/pincel
        cr.fill()
        
        # Obtenemos el valor actual del slider
        x = (self.par.cur_value * 34) / 100
        
        # Dibujamos 34 barritas para el medidor y según el valor de 'x'
        # decidimos si está 'encendida' o no
        for i in range(34):
            if (i < 34 - x):
                cr.set_source_rgb(0.53, 0, 0)
            else:
                cr.set_source_rgb(1, 0, 0)
            
            h = 15 + (i*5)
            cr.rectangle(15,h,49,4)
            cr.fill()
            
            cr.rectangle(67,h,49,4)
            cr.fill()

# Creamos una ventana sencilla en PyGTK con el slider y el canvas
class PyApp(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)
        
        self.set_title('CPU Meter')
        self.set_size_request(200, 200)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('destroy', gtk.main_quit)

        self.cur_value = 10
       
        vbox = gtk.VBox(False, 2)
        
        scale = gtk.VScale()
        scale.set_range(0, 100)
        scale.set_digits(0)
        scale.set_size_request(35, 160)
        scale.set_value(self.cur_value)
        scale.set_inverted(True)
        scale.connect('value-changed', self.on_changed)
        
        self.cpu = Cpu(self)
        
        hbox = gtk.HBox(False)
        hbox.pack_start(self.cpu)
        hbox.pack_start(scale)
        
        vbox.pack_start(hbox, True, True, 2)

        self.add(vbox)
        self.show_all()
        
    # Programamos el evento 'value-changed' de la barra para que con cada
    # cambio mande a redibujar al widget del medidor
    def on_changed(self, widget):
        self.cur_value = widget.get_value()
        self.cpu.queue_draw()


    def get_cur_value(self):
        return self.cur_value

PyApp()
gtk.main()
Al final la aplicación quedó así:



Bastante aceptable para mi gusto. El código pueden descargarlo aquí


Segunda prueba de la semana: PyWebKit

La segunda prueba fue con PyWebKit. Esta prueba me ha frustrado un poco por la dificultad de conseguir documentación o referencias sobre la API de PyWebKit. Fue una labor árdua. Tuve que descargar varios códigos fuentes; entre ellos el de Gwibber (cliente de Twitter) y hasta el del mismísimo PyWebKit para lograr acercame a algo vagamente funcional.

El único ejemplo que traía PyWebKit era el de un navegador que soporta pestañas y otro centenar de características, por lo que no era para nada sencillo comprender su código (¿a quién se le ocurriría poner ese ejemplo? BIG FAIL! Es solo un demo, ¡rayos!... No el próximo Firefox, Opera o Safari).

El código de gwibber era de lejos más comprensible, aunque no menos complejo (se lo justifico por ser una aplicación de verdad no un ejemplo). Después de mucho leer, digerir implementaciones de controles GTK, fumarme unas cuantas lumpias y otros esoterismos, pude dar con el método que permite insertar código HTML directamente sobre el widget... el famoso load_string.

La cuestión con PyWebKit es relativamente simple, porque después que dominamos el load_string lo demás es carpintería HTML y CSS.

El código de la prueba a continuación:

#!/usr/bin/python

# Ejemplo de widget con WebKit
#
# Author: Wil Alvarez (aka Satanas)
# Oct 20, 2009

import gtk
import webkit
import gobject
gobject.threads_init()

# Codigo HTML que insertaremos al control para que lo muestre
ABOUT_PAGE = """
<html><head><title>PyWebKitGtk</title></head><body>
<h1>Mi primera prueba con PyWebKit</h1>
<p><a href="http://code.google.com/p/pywebkitgtk/">http://code.google.com/p/pywebkitgtk/</a><br/>
</p>
<div style="border: 1px solid #000; width:300px; height: 100px; background-color:#aaa;">
  zOMG! This is fucking awesome<br/><br/>
  No se que más poner en este div con estilos css  XDDD
</div>
</body></html>
"""

# Clase donde sobreecribimos el widget WebView de WebKit para implementar
# nuestro código y hacer uso del load_string para inyectar HTML directamente
# sobre el control (sin usar URI o algo similar)
class MessageStreamView(webkit.WebView):
    def __init__(self):
        webkit.WebView.__init__(self)
        self.connect("navigation-requested", self.on_click_link)
        
        self.settings = webkit.WebSettings()
        self.set_settings(self.settings)
        
        # Recibe como parámetros el código HTML, el mime-type de la página,
        # la codificación y un URI
        self.load_string(ABOUT_PAGE, "text/html", "iso-8859-15", "about")
        
    def on_click_link(self, view, frame, req):
        uri = req.get_uri()
        print uri
        return True

# Creamos una ventana simple en PyGTK con el control que acabamos de crear y 
# voilá! Tenemos nuestro widget que renderiza páginas web con el motor WebKit
class Simulador(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)
        self.set_title('Pruebas de Gwibber, Webkit y otras shits')
        self.set_default_size(400, 400)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect('destroy', gtk.main_quit)
        
        messages = MessageStreamView()
        
        vbox = gtk.VBox(False, 5)
        vbox.pack_start(messages, True, True, 0)
        
        self.add(vbox)
        self.show_all()
    
Simulador()
gtk.main()
La ventana luce así:



Y el código pueden descargarlo aquí.

Aún no estoy muy satisfecho con la información que tengo de PyWebKit, lo mejor que he conseguido es una lista con los nombres de los métodos de la API y más nada, así que seguiré buscando.


Conslusiones

Cada librería tiene un ámbito diferente, por lo que la idea no es establecer una comparación uno a uno entre ellas, sino destacar los pro y los contra para forjar una idea de hasta donde podemos llegar con ellas.

PyWebKit

La mayor desventaja de PyWebKit es la falta de documentación. No me importaría tener una documentación vulgar, chapucera y hasta con errores ortográficos, con tal de al menos tener una! Sin embargo, para compensar eso podemos decir que PyWebKit nos otorga todo el poder y la flexibilidad de un motor de renderizado como WebKit. El límite lo pone nuestro manejo de HTML/CSS y el nivel de implementación de PyWebKit.

Cairo

Cairo tampoco tiene documentación ofical (o no la he encontrado aún) pero al menos hay unos cuantos tutoriales en la red que te dan luces acerca de su funcionamiento y sus métodos básicos. La principal ventaja de Cairo es que nos da la oportunidad de ser artistas sobre un widget xD, incluso creo que se puede usar OpenGL para acelerar el renderizado y aplicar efectos 3D... así que el límite lo pone nuestra imaginación. Pero como diría el abuelo de Peter Parker:

Un gran poder trae consigo una gran responsabilidad

Y esto es porque Cairo se utiliza en controles GTK que hacen las veces de lienzo; podemos imaginarlo como un control más de GTK pero que podemos dibujar a nuestro antojo. Esto trae una consecuencia, pensar en una aplicación desarrollada completamente con Cairo requiere una inversión de trabajo enorme (y quizás injustificada e innecesaria) pues Cairo como tal no tiene widgets, así que nos tocaría implementar desde cero cada control que queramos utilizar; entiéndase cajas de texto, etiquetas, botones y ni hablar de las listas y elementos con scroll... sería una verdadera pesadilla.

Ambas librerías tienen un enorme potencial, cada una en su ámbito y creo que la fórmula ganadora está en una buena combinación de ambas. Ni más ni menos.

Para la próxima entrega postearé las segundas pruebas con Cairo y las pruebas con DBus y el sistema de notificaciones de Ubuntu, NotifyOSD.

Referencias
[1] http://www.zetcode.com/tutorials/pygtktutorial/customwidget/
[2] http://zetcode.com/tutorials/cairographicstutorial/customgtkwidget/
[3] http://www.pygtk.org/articles/cairo-pygtk-widgets/cairo-pygtk-widgets.htm
[4] http://www.tortall.net/mu/wiki/CairoTutorial
[5] http://jackvalmadre.wordpress.com/2008/09/21/resizable-image-control/
[6] http://www.pygtk.org/articles/cairo-pygtk-widget-signals-es/cairo-pygtk-widget-signals.html
[7] http://trac.webkit.org/attachment/wiki/HackingGtk/webkit.api

Shoes, Cairo y PyGTK

jueves, febrero 26, 2009

Estuve conversando ayer con un amigo sobre toolkits sencillos y rápidos para desarrollar aplicaciones gráficas multiplataforma. Hablamos de FLTK, de ETK, de murgaLua, obviamente del GTK+ y Qt4 y hasta del mismísimo infame Gambas. Es ahí cuando el pana me presenta a "Shoes" <fanfarria con bombos platillos>.

Shoes (a.k.a alfombra, media, chancleta o como te provoque llamarlo =P) es un toolkit muy pequeño y que por sí solo puede hacer cosas realmente impresionantes. Según su creador "... puedes hacer aplicaciones realmente colorídas..." y se lo creo. Para muestra un botón



Está hecho en Ruby pero su sintaxis es tan simple que parece HTML con CSS (claro gracias en parte a los bloques de código Ruby). La cuestión me llamó tanto la atención que decidí probarlo y mi conclusión: es sencillamente maravilloso. El código es tan limpio, tan simple y tan intuitivo que dan ganas de poner a volar la imaginación para desarrollar cuanta cosa se te ocurra.

Y es que el simple hecho de que pueda hacer una ventana con un fondo degradado con tan solo 7 miserables líneas de código me hace sangrar la nariz.
Shoes.app do
background "#F3F".."#F90"
title "Shoooes", :top => 60,
:align => "center",
:font => "Trebuchet MS",
:stroke => white
end

Sin embargo me surgió una pregunta: ¿Por qué Ruby? y e instantáneamente surgió otra: ¿No habrá un Shoes para Python? La respuesta a la segunda es NO. La respuesta a la primera aún la desconozco.

Si aún están leyendo se preguntarán: ¿Y que tiene que ver todo esto con Cairo y PyGTK? Pues muy simple, revisando el código fuente de Shoes me di cuenta que una ventana no es más que un lienzo (o canvas) y que ese lienzo _aparentemente_ lo crean y lo trabajan con Cairo. Suena muy simple, pero créanme... no lo es.

Fue ahí donde se me ocurrió la idea: ¿Por qué no hacer una implementación de Shoes para Python usando Cairo? y pues bueno aquí estoy, leyendo todo cuanto encuentro sobre Cairo y PyGTK. He leído un par de blogs donde dicen como trabajar con Cairo y PyGTK, además de la documentación de Cairo. Entre ellos:

http://blog.rastersoft.com/index.php/2007/06/20/trabajando-con-pycairo-y-gtk/

http://blog.eikke.com/index.php/ikke/2007/02/17/python_cairo_xshape_and_clocks

http://cairographics.org/documentation/pycairo/reference/index.html#reference-index

Apenas he corrido un par ejemplos y todo parece funcionar muy bien. Seguiré investigando y publicando mis progresos.

Cambio y fuera