16.2. Controles de Lista Desplegable (ComboBox) y Lista Desplegable con Entrada (ComboBoxEntry)

16.2.1. Controles ComboBox

El control ComboBox sustituye el obsoleto OptionMenu con un control potente que utiliza un TreeModel (generalmente un ListStore) que proporciona los elementos de la lista que se mostrarán. El ComboBox implementa la interfaz CellLayout, que proporcina diversos métodos para gestionar la visualización de los elementos de la lista. Uno o más methods for managing the display of the list items. One or more CellRenderers se pueden empaquetar en un ComboBox para personalizar la visualización de los elementos de la lista.

16.2.1.1. Uso Básico de ComboBox

La forma sencilla de crear y poblar un ComboBox es utilizar la función auxiliar:

  combobox = gtk.combo_box_new_text()

Esta función crea una ComboBox y su almacén ListStore asociado y lo empaqueta con un CellRendererText. Los siguientes métodos auxiliares se usan para poblar o eliminar los contenidos de la ComboBox y su ListStore:

  combobox.append_text(text)
  combobox.append_text(text)
  combobox.insert_text(position, text)
  combobox.remove_text(position)

donde text es la cadena que se añadirá a la ComboBox y position es el índice donde se insertará o eliminará el texto text. En la mayoría de los casos las funciones y métodos auxiliares es todo lo que se necesitará.

El programa de ejemplo comboboxbasic.py demuestra el uso de las anteriores funciones y métodos. Figura 16.5, “ComboBox Básica” ilustra el programa en ejecución:

Figura 16.5. ComboBox Básica

ComboBox Básica

Para obtener el texto activo se puede usar el método:

  texto = combobox.get_active_text()
      

Sin embargo, hasta la versión 2.6 no se proporciona en GTK+ un método cómodo para obtener el texto activo. Para ello se podría usar una implementación similar a:

  def get_active_text(combobox):
      model = combobox.get_model()
      active = combobox.get_active()
      if active < 0:
          return None
      return model[active][0]

El índice del elemento activo se obtiene a través del método:

  active = combobox.get_active()

El elemento activo se puede establecer con el método:

  combobox.set_active(index)

donde index es un entero mayor que -2. Si index es -1 no hay elemento activo y el control ComboBox estará en blanco. Si index es menor que -1, la llamada será ignorada. Si index es mayor que -1 el elemento de la lista con dicho índice será mostrado.

Se puede conectar a la señal "changed" de un ComboBox para recibir notificación del cambio del elemento activo. La signatura del manejador de "changed" es:

  def changed_cb(combobox, ...):

donde ... representa cero o más argumentos pasados al método GObject.connect().

16.2.1.2. Uso Avanzado de ComboBox

La creación de una lista ComboBox mediante la función gtk.combo_box_new_text() es aproximadamente equivalente al siguiente código:

  liststore = gtk.ListStore(str)
  combobox = gtk.ComboBox(liststore)
  cell = gtk.CellRendererText()
  combobox.pack_start(cell, True)
  combobox.add_attribute(cell, 'text', 0)  

Para sacar partido de la potencia de las variadas clases de objetos TreeModel y CellRenderer es necesario construir una ComboBox utilizando el constructor:

  combobox = gtk.ComboBox(model=None)

donde model es un modelo TreeModel. Si se crea una lista ComboBox sin asociarle un TreeModel es posible añadirlo a posteriori utilizando el método:

  combobox.set_model(model)

Se puede obtener el TreeModel asociado con el método:

  model = combobox.get_model()

Algunas de las cosas que se pueden hacer con una ComboBox son:

  • Compartir el mismo TreeModel con otras ComboBoxes y TreeViews.
  • Mostrar imágenes y texto en los elementos de la ComboBox.
  • Utilizar un TreeStore o ListStore existente como modelo para los elementos de la lista de la ComboBox.
  • Utilizar un TreeModelSort para disponer de una lista de ComboBox ordenada.
  • Utilizar un TreeModelFilter para usar un subárbol de un TreeStore como fuente de elementos de la lista de la ComboBox.
  • Usar un TreeModelFilter para utilizar un subconjunto de las filas de un TreeStore o ListStore como elementos de la lista de la ComboBox.
  • Utilizar una función de datos de celda para modificar o sintetizar la visualización de los elementos de la lista.

El uso de los objetos TreeModel y CellRenderer se detalla en el capítulo de Controles de Vista de Árbol.

Los elementos de la lista de la ComboBox se pueden mostrar en una tabla si se tiene un gran número de elementos que visualizar. En otro caso, la lista tendrá flechas de desplazamiento si la lista no puede ser mostrada en su totalidad. El siguiente método se usa para determinar el número de columnas que se mostrarán:

  combobox.set_wrap_width(width)

donde width es el número de columnas de la tabla que muestra los elementos de la lista. Por ejemplo, el programa comboboxwrap.py muestra una lista de 50 elementos en 5 columnas. Figura 16.6, “ComboBox con una Disposición Asociada” ilustra el programa en acción:

Figura 16.6. ComboBox con una Disposición Asociada

ComboBox con una Disposición Asociada

Con un gran número de elementos, digamos, por ejemplo, 50, el uso del método set_wrap_width() tendrá un mal rendimiento debido al cálculo de la disposición en tabla. Para tener una noción del efecto se puede modificar el programa comboboxwrap.py en su línea 18 de forma que muestre 150 elementos.

        for n in range(150):

Ejecute el programa para obtener una idea aproximada del tiempo de inicialización. Luego modifíquelo comentando la línea 17:

        #combobox.set_wrap_width(5)

Ejecute y cronometre de nuevo. Debería ejecutarse significativamente más rápido. Unas 20 veces más rápido.

Además del método get_active() antes descrito, se puede obtener un iterador TreeIter que señala la fila activa mediante el método:

  iter = combobox.get_active_iter()

También se puede establecer el elemento de la lista activa usando un iterador TreeIter con el método:

  combobox.set_active_iter(iter)

Los métodos set_row_span_column() y set_column_span_column() permiten la especificación de un número de columna de un TreeModel que contiene el número de filas o columnas que debe abarcar el elemento de la lista en una disposición de tabla. Desgraciadamente, en GTK+ 2.4 estos métodos funcionan mal.

Puesto que ComboBox implementa la interfaz CellLayout, que tiene capacidades similares a las de una TreeViewColumn (véase la sección de TreeViewColumn para más información). En resumen, la interfaz proporciona:

  combobox.pack_start(cell, expand=True)
  combobox.pack_end(cell, expand=True)
  combobox.clear()

Los dos primeros métodos empaquetan un CellRenderer en la ComboBox y el método clear() elimina todos los atributos de todos los CellRenderers.

Los siguientes métodos:

  comboboxentry.add_attribute(cell, attribute, column)

  comboboxentry.set_attributes(cell, ...)

establecen los atributos del CellRenderer indicado por cell. El método add_attribute() toma una cadena de nombre de atributo attribute (p.e. 'text') y un número entero de columna column de la columna en el TreeModel usado, para fijar el atributo attribute. Los argumentos restante del método set_attributes() son pares atributo=columna (p.e. text=1).

16.2.2. Controles ComboBoxEntry

El control ComboBoxEntry sustituye al control Combo. Es una subclase del control ComboBox y contiene un control hijo de entrada Entry que obtiene sus contenidos seleccionando un elemento en la lista desplegable o introduciendo texto directamente en la entrada bien desde el teclado o pegándolo desde un portapapeles Clipboard o una selección.

16.2.2.1. Uso Básico de ComboBoxEntry

Como la ComboBox, la ComboBoxEntry se puede crear con la función auxiliar:

  comboboxentry = gtk.combo_box_entry_new_entry()

Una ComboBoxEntry se debe rellenar utilizando los métodos auxiliares de ComboBox descritos en Uso Básico de ComboBox.

Puesto que un control ComboBoxEntry es un control Bin, su control hijo de entrada Entry está disponible utilizando el atributo "child" o el método get_child():

  entry = comboboxentry.child
  entry = comboboxentry.get_child()

Se puede obtener el texto de la entrada Entry utilizando su método get_text().

Al igual que ComboBox, es posible seguir los cambios del elemento activo de la lista conectándose a la señal "changed". Desgraciadamente, esto no permite seguir los cambios en la entrada Entry que se hacen por entrada directa. Cuando se hace una entrada directa al control Entry se emite la señal "changed", pero el índice devuelto por el método get_active() será -1. Para seguir todos los cambios del texto de la entrada Entry text, será necesario utilizar la señal "changed" de la entrada Entry. Por ejemplo:

  def changed_cb(entry):
      print entry.get_text()

  comboboxentry.child.connect('changed', changed_cb)

imprimirá el texto tras cada cambio en el control hijo Entry. Por ejemplo, el programa comboboxentrybasic.py muestra el uso de la API auxiliar. Figura 16.7, “ComboBoxEntry Básica” ilustra la ejecución del programa:

Figura 16.7. ComboBoxEntry Básica

ComboBoxEntry Básica

Obsérvese que cuando se modifica el texto de la entrada Entry debido a la selección de un elemento de la lista desplegable se llama dos veces al manejador de la señal "changed": una vez cuando se elimina el texto, y, otra cuando el texto se establece desde el elemento seleccionado de la lista.

16.2.2.2. Uso Avanzado de ComboBoxEntry

El constructor de una ComboBoxEntry es:

  comboboxentry = gtk.ComboBoxEntry(model=None, column=-1)

donde model es un TreeModel y column es el número de la columna en el modelo model que se usará para fijar los elementos de la lista. Si no se indica la columna el valor predeterminado es -1 que significa que el texto de la columna no está especificado.

La creación de una ComboBoxEntry utilizando la función auxiliar gtk.combo_box_entry_new_text() es equivalente al siguiente código:

  liststore = gtk.ListStore(str)
  comboboxentry = gtk.ComboBoxEntry(liststore, 0)

La ComboBoxEntry añade un par de métodos que se usan para establecer y recuperar el número de columna del TreeModel que se usará para fijar las cadenas de los elementos de la lista:

  comboboxentry.set_text_column(text_column)
  text_column = comboboxentry.get_text_column()

La columna de texto también se puede obtener y especificar utilizando la propiedad "text-column". Véase la Sección de Uso Avanzado de ComboBox para más información sobre el uso avanzado de una ComboBoxEntry.

Nota

La aplicación debe establecer la columna de texto para que la ComboBoxEntry fije los contenidos de la entrada Entry desde la lista desplegable. La columna de texto únicamente puede determinarse una vez, bien utilizando el constructor o utilizando el método set_text_column().

Al crear una ComboBoxEntry ésta se empaqueta con un nuevo CellRendererText que no es accesible. El atributo 'text' del CellRendererText se establece como un efecto colateral de la determinación de la columna de texto utilizando el método set_text_column(). Se pueden empaquetar CellRenderers adicionales en una ComboBoxEntry para la visualización en la lista desplegable. Véase la Sección de Uso Avanzado de ComboBox para más información.