Saturday, October 4, 2008

MItem

Bueno, pues después de oir las duras críticas de Óscar, Magmax y las mías propias le he hecho algunos cambios al ModelCol. No supone un cambio muy importante, de hecho internamente no hay ningún cambio, solo en el API.

Lo primero es que ha cambiado de nombre, ahora se llama MItem, que viene a ser algo así como "elemento del modelo". Si el modelo es una tabla, el MItem puede describir una columna, que es el caso habitual, pero también se podría ver como una fila si se requiere.

No está ligado a TreeModel o TreeView, ni siquiera a GTK. A pesar de ello, lo voy a describir como el modelo de ListStore para que la explicación no quede tan abstracta. El MItem tiene los siguientes campos (todos opcionales):

  • title (str): Es el título de la cabecera de la columna.

  • render (str): Identifica al encargado de representar el dato en ese columna (en el caso de TreeView, será un CellRenderer). Si no se indica, el render por defecto es "text". El significado real del render depende del encargado de representar el modelo, por ejemplo, el skin_list.

  • id (int). Es una prioridad para esa columna. Lo usamos para crear los índices de ordenación. El "id" más alto es la columna de ordenación por defecto. Si una columna no tiene "id", no se puede ordenar por ella.

  • var (bool). Indica (si es True) que para una misma fila, los valores de esa columna pueden cambiar con el tiempo. Por ejemplo, en un inspector que muestra la lista de unidades de disco, la columna de "espacio libre" sería "var". Si no se indica, es "False" por defecto.

Los parámetros de MItem se indican como un diccionarios (kargs que se dice en Python). A parte de los anteriores, se pueden indicar como clave cualquier palabra que pueda ser utilizada para identificar una propiedad de la columna que describe, en nuestro caso, hasta ahora solo los hemos utilizado para indicar propiedades de los CellRendereres de los TreeViewColumn.

El valor de esas propiedades se puede indicar de dos modos:
  • Por instancia (o fila). En este caso, el identificador va precedido de un guión bajo (_nombre) y el valor es el nombre de un atributo de un objeto del modelo. Cada fila tiene un valor propio para esa propiedad. Suena complicado, pero en los ejemplos vais a ver que es muy sencillo.

  • Fijo (por columna). En este caso, el identificador debe ir precedido de un doble guión bajo (__nombre). El valor indicado se interpreta como un literal y se aplica a toda la columna.

Limitaciones

  • Los nombres de las propiedades no pueden empezar por guión bajo.
  • Los nombres de las propiedades tienen que ser identificadores Python válidos. En el caso de propiedades gobject, como "stock-id" el consumidor del metamodelo deberá hacer las transformaciones oportunas.

Ejemplos:

Voy a poner la versión MItem de los mismos ejemplo que para ModelCol. Si los comparáis veréis que el interfaz es más genérico y para el mismo caso es más corto casi siempre.

MItem(title='Name')
- Etiqueta de la columna: 'Name'
- Renderer: 'text'
- Asignar a la propiedad 'text' del renderer, el valor del atributo 'Name'

MItem(title='Name', _text='filename')
- Asignar a la propiedad 'text' el valor del atributo 'filename'

MItem(title='Name', id=10)
- Ordenar por esta columna, prioridad: 10

MItem(title='Value', _text='val', __xalign=1)
- Asignar a la propiedad 'text' el valor del atributo 'val'
- Justificar el texto de esta columna a la derecha

MItem(title="Level", _markup="level", id=10)
- Asignar a la propiedad 'markup' el valor del atributo 'level'
- Ordenar por esta columna, prioridad: 10

MItem(title='Variable', _text='key',
__background='gray', __background-set=True)
- Asignar a la propiedad 'text' el valor del atributo 'key'
- Fijar color de fondo de esta columna a 'gray'

MItem(render='pixbuf', __stock-id='gtk-file')
- Columna sin título
- Renderer: 'pixbuf'
- Fijar la propiedad 'stock-id' a 'gtk-file' para cualquier fila

MItem(title='Identity', _text='key', _markup='format')
- Asignar a la propiedad 'text' el valor del atributo 'key'
- Asignar a la propiedad 'markup' el valor del atributo 'format'

MItem(render='pixbuf', _stock-id='icon')
- Columna sin título
- Renderer: 'pixbuf'
- Asignar a la propiedad 'stock-id' el valor del atributo 'icon'

MItem(title="Installed", render="toggle", _active='installed',
__activatable=True)
- Renderer: 'toggle'
- Asignar a la propiedad 'active' el valor del atributo 'installed'
- Fijar la propiedad 'activatable' a True para todas las filas

2 comments:

MagMax said...

A ver si lo he entendido... ¿Entonces los _nombre afectan al CellRenderer mientras que los __nombre, al TreeViewColumn, ¿correcto?

Me encontré con el mismo problema y no se me ocurrió un forma tan simple de solucionarlo.

Sin embargo le encuentro un pequeño problemilla, que voy a explicar sólo con el CellRendererToggle (CRT).

El uso habitual de un CRT es tener una cajita activable, lo que implica tres cosas: por un lado, seleccionar la propiedad "activatable"; por otro, indicar la columna que muestra su valor (habitualmente, la propia) y, finalmente, implementar un método que permite cambiar el estado de la celda cuando se produce un evento 'toggled'.

Veo que los dos primeros problemas los has solucionado de manera muy elegante mediante el _active y __activatable, pero... ¿Alguien debe implementarse el método del evento 'toggled' cada vez que quiera una columna de este tipo?

Lo mismo sería aplicable a un radio button, ya que el procesado del evento es completamente distinto: implica recorrer todas las filas desactivando el valor de la columna y seleccionar el valor único (eso si no hay grupos, que lo complicaría aún más).

¿Qué propones en este caso?


Mi solución en otro proyecto fue aceptable, pero no buena: yo sí dependo completamente del TreeView (esto podría arreglarlo sacando la parte dependiente a otra clase), y creando un tipo de MItem por cada objeto que permito. Así, tengo un MCtoggle (MetaColumnToggle) que ya implementa el método. La única parte "inaceptable" del esquema es que necesitaba el modelo para poder acceder a los valores y hacer el toggle, y no se me ocurrió un método mejor de hacerlo (bueno... sí pero no: creando un nuevo evento que me dé el modelo entre los datos, pero al asignarlo tengo que indicar el modelo y estoy en las mismas).

Claro, también tengo un MCgeneric que es abstracto y del que heredan todos los MC*. Como véis, no hay "por defecto", ya que si quieres un toggle se usa un MCtoggle y si quieres un text se usa un MCtext, cada uno con sus propiedades, sus virtudes y sus defectos.

MagMax said...

Ah, se me olvidaba...

Sí, ya sé que este sistema es un poco estático y que no permite crear objetos como se desee, ya que lo que tengo son muchos componentes para usar en mis TVC. Seguramente eso sea inaceptable en algo tan dinámico como TP, pero era un ejemplo de mi solución al problema planteado.