# El diseño modelo/vista/controlador (Python)

El diseño modelo/vista/controlador
Sebastian Pölsterl
sebp@k-d-w.org 2011
Marta Maria Casetti
mmcasetti@gmail.com 2012
Daniel Mustieles
daniel.mustieles@gmail.com 2011 - 2020
Nicolás Satragno
nsatragno@gmail.com 2012 - 2013
Jorge González
jorgegonz@svn.gnome.org 2011

# El diseño modelo/vista/controlador

# Visión general

Tanto los widgets TreeView como los ComboBox se construyen sobre el diseño modelo/vista/controlador. El modelo (una implementación de `Gtk.TreeModel`, generalmente `Gtk.TreeStore` o `Gtk.ListStore`) almacena los datos; la vista (por ejemplo, `Gtk.TreeView`, `Gtk.ComboBox`, o `Gtk.ComboBoxText`) recibe las notificaciones de cambio y muestra el contenido del modelo. El controlador, finalmente, cambia el estado del modelo (a través de algunos métodos en la implementación del modelo, como `append()` o `remove()`) y le notifica a la vista de estos cambios (a través de señales como `«changed»`).

# El modelo

La principal diferencia entre las dos implementaciones primarias de `Gtk.TreeModel` es que `Gtk.ListStore` contiene filas simples de datos sin hijos, mientras que `Gtk.TreeStore` también contiene filas de datos, pero cada fila puede tener filas hijas (que a su vez pueden tener filas hijas, y así sucesivamente).

Los datos en el modelo pueden obtenerse o modificarse usando el «iter» del árbol y el índice de columna, `Gtk.TreeIter`, o `Gtk.TreePath`.

Con el objeto de lista empotrado de Python puede usar `len()` para obtener el número de filas y usar fragmentos para obtener o establecer valores. De lo contrario, el método `append()` devuelve una instancia de `Gtk.TreeIter`, que apunta al lugar de la fila insertada recientemente. También puede obtener un `Gtk.TreeIter` llamando a `get_iter()`.

Como `Gtk.ListStore` sólo contiene un nivel, es decir que los nodos no tienen nodos hijos, una ruta es esencialmente el índice de la fila a la que quiere acceder. En el caso de `Gtk.TreeStore`, una ruta es una lista de índices o una cadena. La forma de la cadena es una lista de números separados por «:». Cada número se refiere al desplazamiento en ese nivel. Entonces, la ruta `«0»` se refiere al nodo raíz y la ruta `«2:4»` al quinto hijo del tercer nodo.

Métodos útiles para un `Gtk.TreeModel`:

`get_iter(ruta)` devuelve una instancia de `Gtk.TreeIter` que apunta a `ruta`. Se espera que sea una lista de números separados por «:», o una tupla. Por ejemplo, la cadena `«10:4:0»` equivale a la tupla `(10, 4, 0)`, ya que las dos crearían una ruta de profundidad 3 apuntando al 11º hijo del nodo raíz, el 5º hijo de ese 11º hijo, y el 1er hijo de ese 5º hijo.

`iter_next(treeiter)` devuelve una instancia de `Gtk.TreeIter` apuntando siguiendo el «treeiter» en el nivel actual o `None` si no hay un «iter» siguiente.

`iter_has_child(treeiter)` devuelve `True` si `treeiter` tiene hijos, `False` de lo contrario.

`iter_children(treeiter)` devuelve una instancia de `Gtk.TreeIter` apuntando al primer hijo de `treeiter` o `None` si `treeiter` no tiene hijos.

`get_iter_first()` devuelve una instancia de `Gtk.TreeIter` apuntando al primer iterador en el árbol (el que está en la ruta `«0»`) o `None` si el árbol está vacío.

Métodos útiles para un `Gtk.ListStore`:

`append(fila)` añade una fila nueva a este «ListStore», donde `fila` puede ser una lista de valores para cada columna; `row` también puede omitirse o ser `None`, y en ese caso se añade una fila vacía. El método devuelve un `Gtk.TreeIter` apuntando a la fila añadida.

`remove(iter)` elimina `iter` del `Gtk.ListStore`, y devuelve `True` si el iter es válido, y `False` si no lo es. Después de eliminarse, `iter` se establece a la siguiente fila válida.

Métodos útiles para un `Gtk.TreeStore`:

`append(padre, fila)` añade una fila nueva a este «TreeStore»; `padre` debe ser un «Gtk.TreeIter» válido. Si «padre» no es `None`, entonces añadirá la fila nueva después del último hijo de «padre», de lo contrario añadirá una fila al nivel superior; `fila` puede ser una lista de valores para cada columna; o puede omitirse o ser `None`; en este último caso se añadirá una fila vacía. El método devuelve un `Gtk.TreeIter` apuntando a la fila añadida.

`remove(iter)` elimina `iter` del `Gtk.ListStore`, y devuelve `True` si el iter es válido, y `False` si no lo es. Después de eliminarse, `iter` se establece a la siguiente fila válida.

# La vista: el caso de «TreeView»

Una vista de árbol muestra la estructura de elementos padres e hijos como un árbol. Consulte este ejemplo.

La `Gtk.TreeViewColumn` se usa para organizar las columnas verticales.

Métodos útiles para un `Gtk.TreeView`:

`set_model(modelo)` establece el modelo para esta vista de árbol. Si ya tiene un modelo, lo eliminará antes de establecer el nuevo. Si el modelo es `None`, entonces quitará el modelo viejo.

`get_model()` devuelve el modelo en el que está basada esta vista de árbol, `None` si no tiene modelo.

`append_column(columna)` añade la `columna` a la lista de columnas.

`get_selection()` obtiene la `Gtk.TreeSelection` asociada a esta vista de árbol.

Métodos útiles para un `Gtk.TreeViewColumn`:

`add_attribute(renderizador, atributo, valor)` añade un mapeo de atributo a esta columna. `atributo` es el parámetro en `renderizador` al que se le asigna el `valor`.

`pack_start(renderizador, expandir)` empaqueta el `renderizador` en el principio de esta columna. Si `expandir` es `False`, entonces no se le asigna a `renderizador` más espacio del que necesita. Cualquier espacio sin utilizar se divide equitativamente entre las celdas en las que «expandir» es `True`.

`pack_end(renderizador, expandir)` añade el `renderizador` al final de esta columna. Si `expandir` es `False`, entonces no se le asigna a `renderizador` más espacio del que necesita. Cualquier espacio sin utilizar se divide equitativamente entre celdas en las que `expandir` es `True`.

`set_sort_column_id(id_columna_ordenada)` establece la columna del modelo por la que debe ordenarse la vista. Esto también hace que se pueda pulsar la cabecera de la columna.

`set_sort_indicator(opción)` establece si se muestra una pequeña flecha en la cabecera de la columna; `opción` puede ser `True` (se muestra el indicador) o `False`.

`set_sort_order(orden)` cambia el orden por el que se ordena la columna; `orden` puede ser `Gtk.SortType.ASCENDING` o `Gtk.SortType.DESCENDING`.

# La vista: el caso de la «ComboBox»

Una `Gtk.ComboBox` permite la selección de un elemento desde un menú desplegable, consulte este ejemplo. Para una lista de opciones textuales, también se puede usar `Gtk.ComboBoxText`, que es más simple. Tanto `Gtk.ComboBox` como `Gtk.ComboBoxText` pueden contener una entrada.

Métodos útiles para un `Gtk.ComboBox`:

El método estático `new_with_entry()` crea una `Gtk.ComboBox` nueva vacía con una entrada; el método estático `new_with_model(modelo)` crea una con el modelo inicializado a `modelo` nueva; y el método estático `new_with_model_and_entry(modelo)` es una combinación de los dos.

`get_active_iter()` devuelve un `Gtk.TreeIter` que apunta al elemento activo actual. Si no existe, devuelve `None`.

`set_model(modelo)` establece el modelo que esta caja combinada usa a `modelo`, y quita un modelo anterior (si existiera). Si `modelo` es `None`, entonces lo quitará. Tenga en cuenta que esta función no limpia los renderizadores de celda.

`set_entry_text_column(columna_texto)` establece la columna del modelo de la que esta caja combinada obtendrá las cadenas a `columna_texto`. La columna `columna_texto` en el modelo de esta caja combinada debe ser del tipo `str` (esto sólo es relevante si esta caja combinada se creó con la propiedad «has-entry» establecida a «True»).

`set_wrap_width(anchura)` establece la anchura del ajuste de línea de esta caja combinada a `anchura`. La anchura de ajuste de línea es básicamente el número preferido de columnas en el que quiere que el diálogo emergente se distribuya en una rejilla.

Métodos útiles para un `Gtk.ComboBoxText`:

El método estático `new_with_entry()` crea una `Gtk.ComboBoxText` nueva vacía con una entrada.

`append_text(texto)` añade `texto` a la lista de cadenas almacenadas en esta caja combinada.

`get_active_text()` devuelve la cadena actualmente activa en esta caja combinada, o `None` si no hay ninguna seleccionada. Si esta caja combinada contiene una entrada, esta función devolverá su contenido (que no será necesariamente un elemento de la lista).

# La vista: los «CellRenderer»

La vista hace uso de `Gtk.CellRenderer` de varios tipos para dibujar los datos.

Implementaciones de `Gtk.CellRenderer` y métodos útiles:

`Gtk.CellRendererText`: muestra texto en una celda

`Gtk.CellRendererToggle`: muestra un interruptor o botón de radio en una celda. Métodos útiles:

`set_active(opción)`: activa o desactiva un renderizador de celda

`get_active()`: devuelve si el renderizador de celda está activado

`set_radio(radio)`: si «radio» es `True`, el renderizador de celda muestra un interruptor de radio (es decir, un interruptor en un grupo mutuamente exclusivo); si es `False`, muestra una casilla de verificación (una opción booleana independiente)

`get_radio()`: devuelve si se están mostrando botones de radio en lugar de casillas de verificación.

`Gtk.CellRendererPixbuf`: muestra una imagen en una celda

`Gtk.CellRendererCombo`: muestra texto en una celda; pero mientras que `Gtk.CellRendererText` ofrece una entrada simple para editar el texto, `Gtk.CellRendererCombo` ofrece un widget `Gtk.ComboBox` para editarlo. Puede usarse con o sin un widget «Gtk.Entry» asociado, dependiendo del valor de la propiedad «has-entry».

`Gtk.CellRendererProgress`: muestra un valor numérico en forma de barra de progreso en una celda; puede mostrar un texto sobre ella

`Gtk.CellRendererSpinner`: muestra una animación giratoria en una celda

`Gtk.CellRendererSpin`: muestra un botón incremental en una celda

`Gtk.CellRendererAccel`: muestra un acelerador de teclado en una celda

# El controlador: la selección

La mayoría de las aplicaciones no solo necesitarán mostrar datos, sino también recibir eventos de entrada de los usuarios. Para hacer esto, simplemente obtenga una referencia a un objeto de selección y conéctela a la señal `«changed»`.

select = tree.get_selection() select.connect("changed", on_tree_selection_changed)

Después, para obtener datos de la fila seleccionada:

def on_tree_selection_changed(selection): model, treeiter = selection.get_selected() if treeiter != None: print "You selected", model[treeiter][0]

Métodos útiles para un `Gtk.TreeSelection`:

`set_mode(tipo)` establece el tipo de la selección, donde «tipo» puede ser

`Gtk.SelectionMode.NONE`: la selección no es posible

`Gtk.SelectionMode.SINGLE`: se puede seleccionar uno o ningún elemento

`Gtk.SelectionMode.BROWSE`: se selecciona exactamente un elemento. En algunas circunstancias, como inicialmente o durante una operación de búsqueda, es posible que ningún elemento esté seleccionado. Lo que realmente se prohíbe es que el usuario deseleccione un elemento actualmente seleccionado excepto si selecciona otro.

`Gtk.SelectionMode.MULTIPLE`: se puede seleccionar cualquier número de elementos. Las pulsaciones cambien el estado de un elemento. Se puede usar la tecla «Ctrl» para agrandar la selección, y «Mayús» para seleccionar entre el foco y el hijo al que apunta. Algunos widgets también pueden permitir arrastrar y soltar para seleccionar un rango de elementos.

`get_selected()` devuelve una tupla `(modelo, treeiter)`, donde `modelo` es el modelo actual y `treeiter` un `Gtk.TreeIter` que apunta a la fila actualmente seleccionada, o «None» si no la hay. El método no funciona si el modo de selección es `Gtk.SelectionMode.MULTIPLE`; en ese caso, use `get_selected_rows()` en su lugar, que devuelve una lista de instancias `Gtk.TreePath` de todas las filas seleccionadas.

# Referencias

GtkTreeModel

GtkTreeView

GtkTreeViewColumn

GtkComboBox

GtkCellRenderer