Esta semana tuve que trabajar en serio con el widget GtkTreeView en Python y se me hizo completamente cuesta arriba hasta que, después de buscar en múltiples tutoriales y pelearme con el código, me vino una epifanía y entendí su complejo pero potente esquema de funcionamiento; así que, en base a la experiencia adquirida, voy a escribir un pequeño tutorial, acompañado de algo de código para ejemplificar.
En GtkTreeView tenemos una clara división entre los datos, la presentación y el motor. Esto es lo que hace que sea un poco lioso al principio, pero también le da una potencia y versatilidad sorprendente.
Lo primero que necesitamos es añadir los módulos gtk, pygtk, gtk.glade y gobject. El último es necesario para los tipos de datos que vamos a manejar.
A continuación tenemos el widget GtkTreeView. Este elemento es el que mostrará nuestra lista de datos. Obtenemos un puntero a dicho objeto mediante GLADE:
treeview = arbol_xml.get_widget("nuestro_gtktreeview")
Ahora vamos a definir los tipos de datos que contendrá cada fila. A ésto se le denomina modelo. Es importante entender que no todos los campos del modelo tienen por qué ser visibles. En este ejemplo vamos a reservar un entero para saber, cuando el usuario marque una opción, cual es la que está escogiendo. También habrá tres cadenas, que contendrán el texto que queremos mostrar en el TreeView y el color de una de ellas.
modelo = gtk.ListStore (gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
Existen dos tipos de modelos: ListStore y TreeStore. Ambos se manejan prácticamente igual, salvo que el segundo permite tener elementos anidados en forma de arbol. La diferencia principal se da a la hora de añadir los datos en sí, como veremos luego.
El siguiente paso es asociar el modelo al TreeView, de manera que éste sepa qué datos almacenar en cada fila:
treeview.set_model(modelo)
Hasta aquí hemos definido los tipos de datos (mediante el modelo), y tenemos el motor (el TreeView), por lo que sólo nos falta la presentación. Para ello tenemos que usar dos clases más para definir cada una de las columnas que mostraremos:
render=gtk.CellRendererText() # renderer para la primera columna columna = gtk.TreeViewColumn ("titulo", render, text=1, background=2) # primera columna de datos treeview.append_column (columna) render2=gtk.CellRendererText() columna2 = gtk.TreeViewColumn ("otro titulo", render2, text=3) # segunda columna de datos treeview.append_column(columna2)
CellRendererText es una clase encargada de mostrar un texto en una columna de un TreeView. En el ejemplo vemos que creamos dos de ellos, uno para cada columna que vamos a mostrar. Luego, creamos una TreeViewColumn que tendrá varios parámetros: el primero es el título que se mostrará en la parte superior del TreeView para dicha columna (si estamos mostrando los nombres de personas, el título podría ser nombre, por ejemplo), y el segundo es la referencia al tipo de CellRenderer que vamos a utilizar para esa columna (en este caso, como es texto, un CellRendererText, pero hay más tipos). El resto de parámetros depende del CellRenderer que estemos utilizando. En el caso del CellRendererText tenemos, entre otros, el parámetro text, que contendrá el texto que queremos mostrar, y el parámetro background, que contiene el color del texto en formato de cadena de texto.
Vemos que en la primera columna le asociamos el valor 1 para el texto y el 2 para el color de fondo, y para la segunda columna usamos el valor 3 para el texto. Estos valores se refieren a la variable correspondiente del modelo TreeStore que generamos unos pasos antes: la variable 0 es el entero, la 1 y la 2 son los dos strings que contendrán, respectivamente, el texto y el color de la primera columna, y la 3 el texto de la segunda columna.
Tras crear cada una de las columnas la añadimos a nuestro TreeView mediante el método append_column, con lo que nuestro TreeView estará listo para ser rellenado con los datos que deseemos. Esto lo hacemos de la siguiente manera:
iterador = modelo.insert(posicion) # el iterador es el objeto que contiene todos los valores de una fila concreta modelo.set_value(iterador,0,0) # el segundo cero es el dato que metemos (un entero) modelo.set_value(iterador,1,"Un texto") modelo.set_value(iterador,2,"#00FFE0") modelo.set_value(iterador,3,"Otro texto")
Estas llamadas se repetirán tantas veces como filas queramos insertar en el TreeView. La variable posicion contendrá el número de fila en la que se insertarán estos datos.
En el caso de utilizar un TreeStore en lugar de un ListStore, el método insert precisaría de un parámetro adicional, antes de la posición, para indicar qué elemento (referenciado mediante un TreeIter) es el padre. Si se pone None, se supondrá que cuelga del padre.
Con ésto ya tenemos nuestro TreeView listo, pero aún nos faltan dos cosas: borrarlo para meter nuevas filas, y saber qué fila es la que un usuario ha escogido. Lo primero es tan simple como llamar al método clear del modelo, con lo que podemos volver al último paso (con los set_values) para introducir las nuevas columnas. Para lo segundo sólo tenemos que hacer:
seleccion,iterador = treeview.get_selection().get_selected()
Si iterador es None, significa que no hay ninguna fila seleccionada. En caso contrario podemos utilizar
seleccion.get_value(iterador,VARIABLE)
para obtener el valor de la columna indicada por VARIABLE. Si usamos el valor 1 nos devolvería el texto de la primera columna; si usamos el valor 2, el color de fondo de la primera columna, y con el valor 3, el texto de la segunda columna. Con el valor 0 nos devolvería un entero, que hasta ahora no hemos utilizado para nada; pero si a dicho entero le asignamos valores consecutivos al crear las filas, nos dirá cual es el número de fila que escogió el usuario.