Las columnas de vista de árbol (TreeViewColumn) y los Visualizadores o Intérpretes de Celda (CellRenderer) colaboran en la visualización de una columna de datos de una vista de árbol (TreeView). La clase TreeViewColumn proporciona el título de columna y un espacio vertical para que los CellRenderer muestren una porción de los datos de los que contiene el almacén del TreeView. Un CellRenderer maneja la visualización de los datos de cada fila y columna dentro de los confines de una TreeViewColumn. Una TreeViewColumn puede contener más de un CellRenderer para proporcionar una visualización de fila similar a la de una HBox. Un uso habitual con múltiples CellRenderer es la combinación de un Visualizador de Imágenes en Celda (CellRendererPixbuf) y un Visuallizador de Texto en Celda (CellRendererText) en la misma columna.
El ejemplo Figura 14.2, “TreeViewColumns con CellRenderers” muestra un ejemplo que ilustra la composición de dos TreeViewColumn, una con dos CellRenderer y la otra con uno sólo:
La aplicación de cada CellRenderer se indica mediante un color de fondo diferenciado: amarillo en el caso de un CellRendererPixbuf, cian para un CellRendererText, y rosa para el otro CellRendererText. Hay que resaltar que el CellRendererPixbuf y el primer CellRendererText están en la misma columna, encabezada con el texto "Pixbuf and Text". El color de fondo del CellRendererText que muestra "Print File" es el color predeterminado que muestra el área de la aplicación en una fila.
Figura 14.2, “TreeViewColumns con CellRenderers” se creó con el programa treeviewcolumn.py.
El tipo del CellRenderer que se necesita para cada caso viene determinado por el tipo de visualización requerida por los datos del modelo de árbol usado. PyGTK posee tres CellRenderer predefinidos:
| CellRendererPixbuf | visualiza imágenes de píxeles (pixbuf) que pueden haber sido creadas por el programa, o tratarse de una imagen de serie predefinida. |
| CellRendererText | visualiza cadenas de texto, así como números que pueden ser convertidos en cadenas (enteros, reales y booleanos incluidos). |
| CellRendererToggle | visualiza un valor booleano como un botón biestado o como un botón de exclusión |
Las propiedades de un CellRenderer determinan la forma en que se visualizarán los datos:
| "mode" | Lectura-Escritura | El modo de edición del CellRenderer. Es uno de estos: gtk.CELL_RENDERER_MODE_INERT (inerte), gtk.CELL_RENDERER_MODE_ACTIVATABLE (activable) o gtk.CELL_RENDERER_MODE_EDITABLE (editable) |
| "visible" | Lectura-Escritura | Si es TRUE se muestra la celda. |
| "xalign" | Lectura-Escritura | La fracción de espacio libre a la izquierda de la celda dentro del intervalo 0.0 a 1.0. |
| "yalign" | Lectura-Escritura | La fracción de espacio libre sobre la celda dentro del intervalo 0.0 a 1.0. |
| "xpad" | Lectura-Escritura | La cantidad de margen a la derecha e izquierda de la celda. |
| "ypad" | Lectura-Escritura | La cantidad de margen sobre y bajo la celda. |
| "width" | Lectura-Escritura | La anchura fija de la celda. |
| "height" | Lectura-Escritura | La altura fija de la celda. |
| "is-expander" | Lectura-Escritura | Si es TRUE la fila tiene descendientes (hijas) |
| "is-expanded" | Lectura-Escritura | Si es TRUE la fila tiene descendientes y se expande para mostrarlas. |
| "cell-background" | Escritura | El color de fondo de la celda indicada como cadena. |
| "cell-background-gdk" | Lectura-Escritura | El color de fondo indicado como gtk.gdk.Color. |
| "cell-background-set" | Lectura-Escritura | Si es TRUE el color de fondo de la celda lo determina este visualizador |
Las propiedades anteriores están disponibles para todas las subclases de CellRenderer. Pero los distintos tipos de CellRenderer también tienen propiedades exclusivas.
Los CellRendererPixbuf tienen estas propiedades:
| "pixbuf" | Lectura-Escritura | El pixbuf que se visualizará (es anulada por "stock-id") |
| "pixbuf-expander-open" | Lectura-Escritura | Pixbuf para el expansor cuando está desplegado. |
| "pixbuf-expander-closed" | Lectura-Escritura | Pixbuf para el expansor cuando está cerrado. |
| "stock-id" | Lectura-Escritura | El stock ID del icono de serie que se visualizará. |
| "stock-size" | Read-Write | El tamaño del icono representado. |
| "stock-detail" | Lectura-Escritura | Detalle de visualización que se proporcionará al motor de temas. |
Los CellRendererText tienen un gran número de propiedades que tratan fundamentalmente con la especificación de estilos:
| "text" | Lectura-Escritura | Texto que se visualizará. |
| "markup" | Lectura-Escritura | Texto con marcas que se visualizará. |
| "attributes" | Lectura-Escritura | Una lista de atributos de estilo que se aplicarán al texto del visualizador. |
| "background" | Escritura | Color de fondo, como cadena de texto. |
| "foreground" | Escritura | Color de primer plano, como cadena de texto. |
| "background-gdk" | Lectura-Escritura | Color de fondo, como gtk.gdk.Color |
| "foreground-gdk" | Lectura-Escritura | Color de primer plano, como gtk.gdk.Color |
| "font" | Lectura-Escritura | Descripción de la fuente, como cadena de texto. |
| "font-desc" | Lectura-Escritura | Descripción de la fuente, como pango.FontDescription. |
| "family" | Lectura-Escritura | Nombre de la familia de la fuente, p.e. Sans, Helvetica, Times, Monospace. |
| "style" | Lectura-Escritura | Estilo de fuente. |
| "variant" | Lectura-Escritura | Variante de la fuente. |
| "weight" | Lectura-Escritura | Peso de la fuente. |
| "stretch" | Lectura-Escritura | Estirado de la fuente. |
| "size" | Lectura-Escritura | Tamaño de la fuente. |
| "size-points" | Lectura-Escritura | Tamaño de la fuente en puntos. |
| "scale" | Lectura-Escritura | Factor de escala de la fuente. |
| "editable" | Lectura-Escritura | Si es TRUE el texto puede ser cambiado por el usuario. |
| "strikethrough" | Lectura-Escritura | Si es TRUE se tacha el texto |
| "underline" | Lectura-Escritura | Estilo de subrayado del texto. |
| "rise" | Lectura-Escritura | Elevación del texto por encima de la línea base (o por debajo si el valor es negativo) |
| "language" | Lectura-Escritura | El idioma del texto, como código ISO. Pango puede utilizarlo como pista al representar el texto. Si no se entiende este parámetro... probablemente es que no se necesita. Solamente disponible a partir de GTK+ 2.4. |
| "single-paragraph-mode" | Lectura-Escritura | Si es TRUE, se deja todo el texto en un único párrafo. Solamente disponible a partir de GTK+ 2.4. |
| "background-set" | Lectura-Escritura | Si es TRUE se aplica el color de fondo. |
| "foreground-set" | Lectura-Escritura | Si es TRUE se aplica el color de primer plano. |
| "family-set" | Lectura-Escritura | Si es TRUE se aplica la familia de la fuente. |
| "style-set" | Lectura-Escritura | Si es TRUE se aplica el estilo de la fuente. |
| "variant-set" | Lectura-Escritura | Si es TRUE se aplica la variante de la fuente. |
| "weight-set" | Lectura-Escritura | Si es TRUE se aplica el peso de la fuente. |
| "stretch-set" | Lectura-Escritura | Si es TRUE se aplica el estirado de la fuente. |
| "size-set" | Lectura-Escritura | Si es TRUE se aplica el tamaño de fuente. |
| "scale-set" | Lectura-Escritura | si es TRUE se escala la fuente. |
| "editable-set" | Lectura-Escritura | Si es TRUE se aplica la editabilidad del texto. |
| "strikethrough-set" | Lectura-Escritura | Si es TRUE se aplica el tachado. |
| "underline-set" | Lectura-Escritura | Si es TRUE se aplica el subrayado de texto. |
| "rise-set" | Lectura-Escritura | Si es TRUE se aplica la elevación del texto. |
| "language-set" | Lectura-Escritura | Si es TRUE se aplica el idioma usado para mostrar el texto. A partir de GTK+ 2.4. |
Casi cada una de las propiedades de CellRendererText posee una propiedad booleana asociada (con el suffijo "-set") que indica si se aplica dicha propiedad. Esto permite fijar globalmente una propiedad y activar o desactivar su aplicación selectivamente.
Los CellRendererToggle poseen las siguientes propiedades:
| "activatable" | Lectura-Escritura | Si es TRUE, el botón biestado se puede activar. |
| "active" | Lectura-Escritura | Si es TRUE, el botón está activo. |
| "radio" | Lectura-Escritura | Si es TRUE, se dibuja el botón como un botón de exclusión. |
| "inconsistent" | Lectura-Escritura | Si es TRUE, el botón está en un estado inconsistente. A partir de GTK+ 2.2. |
Las propiedades se pueden fijar para todas las filas utilizando el método gobject.set_property(). Véase el programa treeviewcolumn.py como ejemplo del uso de este método.
Un atributo asocia una columna de un modelo de árbol a una propiedad de un CellRenderer. El CellRenderer fija la propiedad en función del valor de una columna de la fila antes de representar la celda. Esto permite personalizar la visualización de la celda utilizando los datos del modelo de árbol. Se puede añadir un atributo al conjunto actual con:
treeviewcolumn.add_attribute(cell_renderer, attribute, column) |
donde la propiedad especificada por attribute se fija para el cell_renderer en la columna column. Por ejemplo:
treeviewcolumn.add_attribute(cell, "cell-background", 1) |
establece el fondo del CellRenderer al color indicado por la cadena de la segunda columna del almacén de datos.
Para eliminar todos los atributos y establecer varios atributos nuevos de una vez se usa:
treeviewcolumn.set_attributes(cell_renderer, ...) |
donde los atributos de cell_renderer se determinan mediante pares clave-valor: propiedad=columna. Por ejemplo, en el caso de un CellRendererText:
treeviewcolumn.set_attributes(cell, text=0, cell_background=1, xpad=3) |
indica, para cada fila, el texto en la primera columna, el color de fondo en la segunda y el margen horizontal desde la cuarta columna. Véase el programa treeviewcolumn.py para ver ejemplos del uso de estos métodos.
Los atributos de un CellRenderer se pueden limpiar utilizando:
treeviewcolumn.clear_attributes(cell_renderer) |
Si no es suficiente el uso de atributos para cubrir nuestras necesidades, también es posible indicar una función que será llamada en cada fila y que determine las propiedades del CellRenderer utilizando:
treeviewcolumn.set_cell_data_func(cell_renderer, func, data=None) |
donde func tiene la signatura:
def func(column, cell_renderer, tree_model, iter, user_data) |
donde column es la TreeViewColumn que contiene el visualizador cell_renderer, tree_model es el almacén de datos e iter es un iterador TreeIter que apunta a una fila en tree_model. user_data es el valor de data que se pasó a set_cell_data_func().
En func se establecen las propiedades que se deseen para cell_renderer. Por ejemplo, el siguiente fragmento de código establece la propiedad de texto de manera que muestre los objetos de PyGTK como una cadena de identificación ID.
...
def obj_id_str(treeviewcolumn, cell_renderer, model, iter):
pyobj = model.get_value(iter, 0)
cell.set_property('text', str(pyobj))
return
...
treestore = gtk.TreeStore(object)
win = gtk.Window()
treeview = gtk.TreeView(treestore)
win.add(treeview)
cell = CellRendererText()
tvcolumn = gtk TreeViewColumn('Object ID', cell)
treeview.append_column(tvcolumn)
iter = treestore.append(None, [win])
iter = treestore.append(iter, [treeview])
iter = treestore.append(iter, [tvcolumn])
iter = treestore.append(iter, [cell])
iter = treestore.append(None, [treestore])
...
|
El resultado debería ser algo como Figura 14.3, “Función de Datos de Celda”:
Otro posible uso de la función de datos de celda es el control del formato de visualización de un texto numérico, p.e. un valor real. Un CellRendererText hará una conversión de forma automática del valor real a una cadena, pero con el formato predeterminado "%f".
Con funciones de datos de celda se pueden generar incluso los datos de las celdas a partir de datos externos. Por ejemplo, el programa filelisting.py usa un almacén ListStore con una única columna que contiene una lista de nombres de archivos. La TreeView muestra columnas que incluyen una imagen pixbuf, el nombre de archivo y su tamaño, modo y fecha del último cambio. Los datos son generados por las siguientes funciones de datos de celda:
def file_pixbuf(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
if stat.S_ISDIR(filestat.st_mode):
pb = folderpb
else:
pb = filepb
cell.set_property('pixbuf', pb)
return
def file_name(self, column, cell, model, iter):
cell.set_property('text', model.get_value(iter, 0))
return
def file_size(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
cell.set_property('text', filestat.st_size)
return
def file_mode(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
cell.set_property('text', oct(stat.S_IMODE(filestat.st_mode)))
return
def file_last_changed(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
cell.set_property('text', time.ctime(filestat.st_mtime))
return
|
Estas funciones obtienen la información de los archivos utilizando el nombre, extraen los datos necesarios y establecen las propiedades de celda 'text' o 'pixbuf' con los datos. Figura 14.4, “Ejemplo de Listado de Archivos Utilizando Funciones de Datos de Celda” muestra el programa de ejemplo en acción:
Un CellRendererText puede utilizar etiquetas de marcado de Pango (estableciendo la propiedad "markup") en vez de una cadena de texto sencilla para codificar diversos atributos de texto y proporcionar una visualización rica, con múltiples cambios de estilos de fuente. Véase la referencia Pango Markup en el Manual de Referencia de PyGTK para obtener más detalles sobre el lenguaje de marcado de Pango.
El siguiente fragmento de código ilustra el uso de la propiedad "markup":
...
liststore = gtk.ListStore(str)
cell = gtk.CellRendererText()
tvcolumn = gtk.TreeViewColumn('Pango Markup', cell, markup=0)
...
liststore.append(['<span foreground="blue"><b>Pango</b></span> markup can'
' change\n<i>style</i> <big>size</big>, <u>underline,'
<s>strikethrough</s></u>,\n'
'and <span font_family="URW Chancery L"><big>font family '
'e.g. URW Chancery L</big></span>\n<span foreground="red">red'
' foreground and <span background="cyan">cyan background</span></span>'])
...
|
produce un resultado semejante a Figura 14.5, “Etiquetas de Marcado para CellRendererText”:
Si se crean etiquetas de marcado sobre la marcha es preciso tener cuidado y sustituir los caracteres con especial significado en el lenguaje de marcas: "<", ">", "&". La función de la biblioteca de Python cgi.escape() permite hacer estas conversiones básicas.
Las celdas CellRendererText pueden hacerse editalbes de forma que una usuaria pueda editar los contenidos de la celda que seleccione haciendo clic en ella o pulsando las teclas Return, Enter, Space o Shift+Space. Se hace editable un CellRendererText en todas sus filas estableciendo su propiedad "editable" a TRUE de la siguiente manera:
cellrenderertext.set_property('editable', True)
|
Se pueden establecer individualmente celdas editables añadiendo un atributo a la TreeViewColumn utilizando un CellRendererText parecido a:
treeviewcolumn.add_attribute(cellrenderertext, "editable", 2) |
que establece que el valor de la propiedad "editable" se indica en la tercera columna del almacén de datos.
Una vez que la edición de la celda termina, la aplicación debe gestionar la señal "edited" para obtener el nuevo texto y establecer los datos asociados del almacén de datos. De otro modo, el valor de la celda recuperará su valor inicial. La signatura del manejador de llamada de la señal "edited" es:
def edited_cb(cell, path, new_text, user_data) |
donde cell es el CellRendererText, path es el camino de árbol (como cadena) a la fila que contiene la celda editada, new_text es el texto editado y user_data son datos de contexto. Puesto que se necesita el TreeModel para usar el camino path y establecer new_text en el almacén de datos, probablemente se quiera pasar el TreeModel como user_data en el método connect():
cellrenderertext.connect('edited', edited_cb, model)
|
Si se tienen dos o más celdas editables en una fila, se podría pasar el número de columna del TreeModel como parte de los datos adicionales user_data así como el modelo TreeModel:
cellrenderertext.connect('edited', edited_cb, (model, col_num))
|
Así, se puede establecer el nuevo texto en el manejador de la señal "edited" de una forma parecida al siguiente ejemplo que usa un almacén de lista ListStore:
def edited_cb(cell, path, new_text, user_data):
liststore, column = user_data
liststore[path][column] = new_text
return
|
Los botones de CellRendererToggle se pueden hacer activables estableciendo la propiedad "activatable" como TRUE. De forma parecida a la celdas editables de CellRendererText la propiedad "activatable" se puede fijar para un conjunto completo de celdas con CellRendererToggle utilizando el método set_property() o individualmente en algunas celdas añadiendo un atributo a la columna TreeViewColumn que contiene el CellRendererToggle.
cellrenderertoggle.set_property('activatable', True)
treeviewcolumn.add_attribute(cellrenderertoggle, "activatable", 1)
|
La creación de botones individuales biestado se puede deducir de los valores de una columna de un TreeModel añadiendo un atributo de manera similar a este ejemplo:
treeviewcolumn.add_attribute(cellrenderertoggle, "active", 2) |
Se debe conectar a la señal "toggled" para disponer de notificación de las pulsaciones del usuario en los botones biestado, de manera que la aplicación pueda modificar los valores del almacén de datos. Por ejemplo:
cellrenderertoggle.connect("toggled", toggled_cb, (model, column))
|
La retrollamada tiene la signatura:
def toggled_cb(cellrenderertoggle, path, user_data) |
donde path es el camino de árbol, como cadena, que apunta a la fila que contiene el botón biestado que ha sido pulsado. Es recomendable pasar el TreeModel y, tal vez, el índice de la columna como parte de los datos de usuario user_data para proporcionar el contexto necesario para establecer los valores del almacén de datos. Por ejemplo, la aplicación puede conmutar los valores del almacén de datos, así:
def toggled_cb(cell, path, user_data):
model, column = user_data
model[path][column] = not model[path][column]
return
|
Si la aplicación desea mostrar los botones biestado como botones de exclusión y que únicamente uno de ellos esté activo, tendrá que recorrer los datos del almacén para desactivar el botón de exclusión activo y posteriormente activar el botón biestado. Por ejemplo:
def toggled_cb(cell, path, user_data):
model, column = user_data
for row in model:
row[column] = False
model[path][column] = True
return
|
usa la estrategia "vaga" de poner todos los valores a FALSE antes de fijar el valor TRUE en la fila especificada en path.
El programa cellrenderer.py ilustra la utilización de celdas CellRendererText editables y de celdas CellRendererToggle activables en un almacén TreeStore.
1 #!/usr/bin/env python
2 # vim: ts=4:sw=4:tw=78:nowrap
3 """ Demonstration using editable and activatable CellRenderers """
4 import pygtk
5 pygtk.require("2.0")
6 import gtk, gobject
7
8 tasks = {
9 "Buy groceries": "Go to Asda after work",
10 "Do some programming": "Remember to update your software",
11 "Power up systems": "Turn on the client but leave the server",
12 "Watch some tv": "Remember to catch ER"
13 }
14
15 class GUI_Controller:
16 """ The GUI class is the controller for our application """
17 def __init__(self):
18 # establecer la ventana principal
19 self.root = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
20 self.root.set_title("CellRenderer Example")
21 self.root.connect("destroy", self.destroy_cb)
22 # Obtener el modelo y vincularlo a la vista
23 self.mdl = Store.get_model()
24 self.view = Display.make_view( self.mdl )
25 # Añadir la vista a la ventana principal
26 self.root.add(self.view)
27 self.root.show_all()
28 return
29 def destroy_cb(self, *kw):
30 """ Destroy callback to shutdown the app """
31 gtk.main_quit()
32 return
33 def run(self):
34 """ run is called to set off the GTK mainloop """
35 gtk.main()
36 return
37
38 class InfoModel:
39 """ The model class holds the information we want to display """
40 def __init__(self):
41 """ Sets up and populates our gtk.TreeStore """
42 self.tree_store = gtk.TreeStore( gobject.TYPE_STRING,
43 gobject.TYPE_BOOLEAN )
44 # colocar los datos globales de la gente en la lista
45 # formamos un árbol simple.
46 for item in tasks.keys():
47 parent = self.tree_store.append( None, (item, None) )
48 self.tree_store.append( parent, (tasks[item],None) )
49 return
50 def get_model(self):
51 """ Returns the model """
52 if self.tree_store:
53 return self.tree_store
54 else:
55 return None
56
57 class DisplayModel:
58 """ Displays the Info_Model model in a view """
59 def make_view( self, model ):
60 """ Form a view for the Tree Model """
61 self.view = gtk.TreeView( model )
62 # configuramos el visualizador de celda de texto y permitimos
63 # la edición de las celdas.
64 self.renderer = gtk.CellRendererText()
65 self.renderer.set_property( 'editable', True )
66 self.renderer.connect( 'edited', self.col0_edited_cb, model )
67
68 # Se configura el visualizador de botones biestado y permitimos que se
69 # pueda cambiar por el usuario.
70 self.renderer1 = gtk.CellRendererToggle()
71 self.renderer1.set_property('activatable', True)
72 self.renderer1.connect( 'toggled', self.col1_toggled_cb, model ) 73
74 # Conectamos la columna 0 de la visualización con la columna o de nuestro modelo
75 # El visualizador mostrará lo que haya en la columna 0
76 # de nuestro modelo.
77 self.column0 = gtk.TreeViewColumn("Name", self.renderer, text=0) 78
79 # El estado de activación del modelo se vincula a la segunda columna
80 # del modelo. Así, cuando el modelo dice True entonces el botón
81 # se mostrará activo, es decir, encendido.
82 self.column1 = gtk.TreeViewColumn("Complete", self.renderer1 )
83 self.column1.add_attribute( self.renderer1, "active", 1)
84 self.view.append_column( self.column0 )
85 self.view.append_column( self.column1 )
86 return self.view
87 def col0_edited_cb( self, cell, path, new_text, model ):
88 """
89 Called when a text cell is edited. It puts the new text
90 in the model so that it is displayed properly.
91 """
92 print "Change '%s' to '%s'" % (model[path][0], new_text)
93 model[path][0] = new_text
94 return
95 def col1_toggled_cb( self, cell, path, model ):
96 """
97 Sets the toggled state on the toggle button to true or false.
98 """
99 model[path][1] = not model[path][1]
100 print "Toggle '%s' to: %s" % (model[path][0], model[path][1],)
101 return
102
103 if __name__ == '__main__':
104 Store = InfoModel()
105 Display = DisplayModel()
106 myGUI = GUI_Controller()
107 myGUI.run()
|
El programa proporcional celdas editables en la primera columna y celdas activables en la segunda columna. Las líneas 64-66 crean un CellRendererText editable y conectan la señal "edited" a la retrollamada col0_edited_cb() (líneas 87-94), que cambia el valor en la columna correspondiente de la fila en el almacén de árbol TreeStore. De la misma manera, las líneas 70-72 crean un CellRendererToggle activable y conectan la señal "toggled" a la retrollamada col1_toggled_cb() (líneas 95-101) para cambiar el valor de la fila correspondiente. Cuando se modifica una celada editable o activable se muestra un mensaje para indicar cuál ha sido el cambio.
Figura 14.6, “Celdas Editables y Activables” ilustra el programa cellrenderer.py en ejecución.