# The Model/View/Controller design (Python)

El diseño modelo/vista/controlador
Sebastian Pölsterl
sebp@k-d-w.org 2011
Marta Maria Casetti
mmcasetti@gmail.com 2012

# 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

# The Controller: the Selection

Most applications will need to not only deal with displaying data, but also receiving input events from users. To do this, simply get a reference to a selection object and connect to the `"changed"` signal.

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

Then to retrieve data for the row selected:

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

Método útil para un `Gtk.TreeSelection`:

`set_mode(type)` sets the type of selection, where type is one of

`Gtk.SelectionMode.NONE` - no selection is possible

`Gtk.SelectionMode.SINGLE` - zero or one element may be selected

`Gtk.SelectionMode.BROWSE` - exactly one element is selected. In some circumstances, such as initially or during a search operation, it’s possible for no element to be selected. What is really enforced is that the user can’t deselect a currently selected element except by selecting another element.

`Gtk.SelectionMode.MULTIPLE` -any number of elements may be selected. Clicks toggle the state of an item. Any number of elements may be selected. The Ctrl key may be used to enlarge the selection, and Shift key to select between the focus and the child pointed to. Some widgets may also allow Click-drag to select a range of elements.

`get_selected()` returns a tuple `(model, treeiter)`, where `model` is the current model and `treeiter` a `Gtk.TreeIter` pointing to the currently selected row, or None if no rows are selected. The method does not work if the selection mode is set to `Gtk.SelectionMode.MULTIPLE`; in that case, use `get_selected_rows()` instead, which Returns a list of `Gtk.TreePath` instances of all selected rows.

# Referencias

GtkTreeModel

GtkTreeView

GtkTreeViewColumn

GtkComboBox

GtkCellRenderer

The Python Gtk+ 3 Tutorial - Tree and List Widgets

The Python Gtk+ 3 Tutorial - CellRenderers

The Python Gtk+ 3 Tutorial - ComboBox