enero 29, 2014

Nuestro propio widget con PyCairo

Estos días estaba jugando con Cairo, debido a la curiosidad de saber de como hacen aquellos widgets que no encuentras en un editor de interfaz. Un ejemplo es el ecualizador de Clementine.

En fin, hay muchas formas de hacerlo, y Cairo me llamo la atención, ya que se integra con Python y GTK. Mi idea fue crear un progress bar algo distinto (sólo con fines de probar), en el cuál si cargaba hasta el 50% se mostrara en rojo, si cargaba hasta el 80% se mostrara en amarillo y el resto en azul. Y al completarse todo la carga, se muestre un texto "100% completo" sobre un fondo degradado:




Para ello trabaje, no enrrollandome con puro código gtk para crear una ventanita, sino usando glade.


Aquí use aquella herramienta que no había usado hasta ahora, el drawingarea.

Bueno el código quedó de la siguiente  forma:
#!/usr/bin/python
# -*- coding: utf-8 -*- 
import gtk
import glib
import cairo


class App(object):
    def __init__ (self):
        #Iniciamos el porcentaje de carga
        self.percent = 0
        
        #Se carga el archivo glade
        builder = gtk.Builder()
        builder.add_from_file('Final.ui')
        
        #Se recuperan los widget a usar
        self.winMain = builder.get_object('window1')
        self.drawingarea = builder.get_object('drawingarea1')
        
        #Instanciamos nuestro widget
        self.pbBGR = ProgressBarBGR(self.drawingarea)
        
        #Conectamos las señales y mostramos la ventana
        builder.connect_signals(self)
        self.winMain.show()
    
    def on_btnIniciar_clicked(self, *args):
        #Ejecutamos el mètodo cada 20ms
        glib.timeout_add(20, self.on_timer) 
        
    def on_timer(self):
        #Incrementamos el porcentaje
        self.percent += 1
        
        #Trabajamos con nuestro widget
        self.pbBGR.setPercent(self.percent) # INSERTAMOS PORCENTAJE
        self.pbBGR.draw()  # DIBUJAMOS
        
        if self.percent > 99:
            self.percent = 0
            return False
        return True


class ProgressBarBGR():
    def __init__(self, drawingarea):
        self.__percent = 0  # Ocultamos nuestra variable
        self.drawingarea = drawingarea
        self.drawingarea.connect("expose-event", self.expose)
        
    def setPercent(self, percent):
        self.__percent = percent
        
    def getPercent(self):
        return self.__percent
    
    def draw(self):
        self.drawingarea.queue_draw()

    def expose(self, widget, event):
        #Creamos el contexto       
        cr = widget.window.cairo_create()
        
        #Determinamos eje x, eje y, ancho y alto del área de dibujo
        x, y = event.area.x, event.area.y
        width, height = event.area.width, event.area.height

        #Dibujamos el rectángulo principal
        cr.set_line_width(2)
        cr.rectangle(x, y, width, height)
        cr.set_source_rgb(0, 0, 0)
        cr.fill()
        
        #Dibujamos los 100 rectángulos y ponemos colores
        h = height
        w = width / 100.0
        perc = self.getPercent()
        if perc > 0:
            for i in range(0,100):
                if i <= perc and i < 50:
                    cr.set_source_rgb(1, 0, 0)
                elif i <= perc and i >= 50 and i <=80:
                    cr.set_source_rgb(1, 1, 0)
                elif i <= perc and i > 80 and i <=99:
                    cr.set_source_rgb(0, 0.5, 0.9)
                else:
                    cr.set_source_rgb(0, 0, 0)
                cr.rectangle( w * i, 0, w -1, h)
                cr.fill()
                
        #Dibujamos la barra completada al 100%
        if perc == 100:
            #Degradamos
            lg = cairo.LinearGradient(x, y, x, height)
            lg.add_color_stop_rgba(0.1, 0, 0, 0, 1)
            lg.add_color_stop_rgba(0.5, 0, 0.6, 1, 1)
            lg.add_color_stop_rgba(0.9, 0, 0, 0, 1)
            
            cr.rectangle(x, y, width, height)
            cr.set_source(lg)
            cr.fill()
            
            #Insertamos el texto de 100%
            cr.select_font_face("Serif", cairo.FONT_SLANT_NORMAL, 
            cairo.FONT_WEIGHT_BOLD)
            if height < 100:
                cr.set_font_size( height/2.5)
            else:
                cr. set_font_size((width + height) / 20)
            texto = "100% COMPLETO"
            (x, y, txtwidth, txtheight, dx, dy) = cr.text_extents(texto)
            cr.set_source_rgb(0.5, 0.5, 0.5)
            cr.move_to(width/2 - txtwidth/2, h/2 + txtheight/2)
            cr.show_text(texto)
            cr.set_source_rgb(1, 1, 1)
            cr.move_to(width/2 - txtwidth/2 + 2, h/2 + txtheight/2 +2 )
            cr.show_text(texto)
            
        
if __name__ == "__main__":
    App()
    gtk.main()

PD: esto fue desarrollado con PyGTK, facilmente se puede cambiar el código a PyGI. Por ejemplo:
en vez de usar  "expose-event" se podría usar "draw". Es cuestión de revisar la documentación.

No hay comentarios:

Publicar un comentario