Marco Islas Blog




Widgets 'recortados' en Gtk

La mayoria de los widgets en Gtk tienen una ventana asociada, y no precisamente la ventana con decoracion que todo mundo ve. Gtk adiere una propiedad llama "window" a los widgets al ser empacados, o emparentados a otro widget contenedor.
Generalmente estos widgets son de apariencia rectangular, pero es posible tunearlos para que tengan la forma que nosotros queremos. La forma mas facil de hacerlo es tomar una imagen un usarla como "Molde" para crar nuestro contenedor recortado. aunque tambien es posible dibujar lo que nosotros querramos usando las funciones de cairo sobre un contexto (Que en si, es lo que hacemos con la imagen molde, pero mucho mas sencillo).
Lo que hacemos es obtener un molde a partir de una imagen, es decir, abrimos la imagen y creamos un gtk.gdk.Pixbuf, que es el mostraremos, pero podemos obtener el gtk.gdk.Pixmap y la mascara de este Pixbuf de forma que podamos usar dicha mascara para crear el contorno de nuestro widget.
Los widgets, una vez empacados,como ya habia dicho obtienen una propiedad llamada window, que pertenece a la clase gtk.gdk.Window. Aquellos que ya se han puesto a dibuar algo con cairo se habran dado cuenta que se obtiene un contexto de un widget a partir de su gtk.gdk.Window.
Bien. tambien es posible obtener este contexto de cairo a partir de un pixmap, hacer el dibujo molde y luego pegarselo a la ventana para que gtk.gdk sepa que partes ha de dibujar y cuales no.
Veamos un pequenio ejemplo.
class shapedWindow(gtk.DrawingArea):
        def __init__(self):
                gtk.DrawingArea.__init__(self)
                self.__pixbuf =  gtk.gdk.pixbuf_new_from_file('./logo.png')
                self.connect('size-allocate',self.size_allocated)
                self.connect('expose-event',self.do_expose_event)
                self.set_size_request(self.__pixbuf.get_width(),
                                self.__pixbuf.get_height())
       
        def size_allocated(self,win,allocation):
                w,h = (allocation.width, allocation.height)
                self.bitmap = gtk.gdk.Pixmap(None,w,h,1)
                context = self.bitmap.cairo_create()
               
                self.do_expose_event(self,'',context)
                parent = self.get_parent()
                win.shape_combine_mask(self.bitmap,0,0)
                parent.shape_combine_mask(self.bitmap,0,0)
               
        def do_expose_event(self, widget, event,allocate = False):
                if allocate:
                        context = allocate
                else:
                        context = self.window.cairo_create()
                if allocate:
                        context.set_operator(cairo.OPERATOR_DEST_OUT)
                        w,h = (self.allocation.width, self.allocation.height)
                        context.rectangle(0,0,w,h)
                        context.set_source_rgb(1,1,1)
                        context.paint()
                context.move_to(0,0)
                context.set_operator(cairo.OPERATOR_OVER)
                if allocate:
                        pixmap,mask = self.__pixbuf.render_pixmap_and_mask()
                        context.set_source_pixmap(mask,0,0)
                else:
                        context.set_source_pixbuf(self.__pixbuf,0,0)
                context.paint()
       
if __name__ == '__main__':
        window = gtk.Window()
        window.set_decorated(False)
        a = shapedWindow()
        window.add(a)
        window.show_all()
        gtk.main()
       
 
Que es lo que hacemos aqui? bien, primero creamos un widget personalizado usando gtk.DrawingArea y conectamos la sennial size-allocate para poder establecer el tamanio de nuestro widget. Una vez llamada esta funcion creamos un pixmap vacio del tamanio de nuestro widget, que es el tamanio que nos ha dado el contenedor padre, este es un rectangulo como de costubre, con un ancho y alto. A este pixmap le sacaramos el cairo context, sobre el cual hemos de 'dibujar' nuestro molde.
Como en este ejemplo el widget y su molde de la misma forma estoy aprovechado el metodo do_expose_event para hacer el dibujo inicial y despues hacer las funciones de redibujado en caso de un evento de expose.
Quienes hacen la chamba aqui? bien, para el dibujo inicial de nuestro widget es context.set_source_from_pixmap(), aunque podriamos usar el mismo set_source_from_pixbuf he detectado problemas con colores en Windows, entonces no lo recomiendo.
Otro que entra en juego y es el que le dice al widget 'orale cabron, apegate a esta forma' es win.shape_combine_mask(self.bitmap,0,0).
De ahi, el ponerle contenido a nuestro widget no es mas que un amanipulacion de colores y demas dentro de nuestro contexto cairo. Que, es otro tema del que hablar, como 'dibujar' lo que queremos en nuestro widget usando su contexto cairo.

Shaped Window widget
Tamanio Completo

Ciertamente, el logtipo de Christine es el widget 'recortado' :-)

Leave a Comment

Write the captcha code you are seeing.

Comment XML feeds: RSS | Atom




twitter logo




Recent Comments On Blog

avatar
Marco Antonio Islas Cruz on
 
avatar
Marco Antonio Islas Cruz on
 
avatar
Marco Antonio Islas Cruz on
 Getting ready
avatar
Marco Antonio Islas Cruz on
 Python: Create win32 services using Python and py2exe
avatar
jopython@gmail.com on
 Python: Create win32 services using Python and py2exe
avatar
yodenuevo on
 Holy Shit!
avatar
markuz on
 Holy Shit!
avatar
yo on
 Holy Shit!
avatar
Gustavo on
 Things that happen last week
avatar
Marco Antonio Islas Cruz on
 Christine: rola_christine.py