9.10. Botones Aumentar/Disminuir

El control SpinButton (Botón Aumentar/ Disminuir) se usa generalmente para permitir al usuario seleccionar un valor dentro de un rango de valores numéricos. Consiste en una caja de entrada de texto con botones de flecha arriba y abajo a un lado. Seleccionando uno de los botones causa que el valor aumente o disminuya en el rango de valores posibles. La caja de entrada también puede editarse directamente para introducir un valor específico.

El SpinButton permite que el valor tenga cero o más cifras decimales y puede incrementarse/decrementarse en pasos configurables. La acción de mantener pulsado uno de los botones opcionalmente provoca una aceleración en el cambio del valor correspondiente al tiempo que se mantenga presionado.

El SpinButton usa un objeto Adjustment (Ajuste) para almacenar la información del rango de valores que el botón aumentar/disminuir puede tomar. Esto le hace un control muy útil.

Recordemos que un control Adjustment (Ajuste) se crea con la siguiente función, que muestra la información que almacena:

  adjustment = gtk.Adjustment(value=0, lower=0, upper=0, step_incr=0, page_incr=0, page_size=0)

Estos atributos de un Adjustment (Ajuste) se usan en el SpinButton de la siguiente manera:

valuevalor inicial para el Botón Aumentar/Disminuir
lowerel valor más bajo del rango
upperel valor más alto del rango
step_incrementvalor que se incrementa/decrementa cuando se pulsa el botón-1 del ratón en un botón
page_incrementvalor que se incrementa/decrementa cuando se pulsa el botón-2 del ratón en un botón
page_sizeno se usa

Adicionalmente, el botón del ratón botón-3 se puede usar para saltar directamente a los valores upper (superior) y lower (inferior) cuando se usa para seleccionar uno de los botones. Veamos como crear un SpinButton (Botón Aumentar/Disminuir):

  spin_button = gtk.SpinButton(adjustment=None, climb_rate=0.0, digits=0)

El argumento climb_rate (razón de escalada) puede tomar un valor entre 0.0 y 1.0 e indica la cantidad de aceleración que el SpinButton tiene. El argumento digits especifica el número de cifras decimales que se mostrarán.

Un SpinButton se puede reconfigurar después de su creación usando el siguiente método:

  spin_button.configure(adjustment, climb_rate, digits)

La variable spin_button especifica el botón aumentar/disminuir que se va a reconfigurar. Los otros argumentos son los mismos que antes.

El adjustment (ajuste) se puede fijar y recupar independientemente usando los siguientes dos métodos:

  spin_button.set_adjustment(adjustment)

  adjustment = spin_button.get_adjustment()

El número de cifras decimales también se puede cambiar usando:

  spin_button.set_digits(digits)

El valor que un SpinButton está mostrando actualmente se puede cambiar usando el siguiente método:

  spin_button.set_value(value)

El valor actual de un SpinButton se puede recuperar como un valor real o como un valor entero usando los siguientes métodos:

  float_value = spin_button.get_value()

  int_value = spin_button.get_value_as_int()

Si quieres alterar el valor de un SpinButton relativo a su valor actual, entonces usa el siguiente método:

  spin_button.spin(direction, increment)

El parámetro direction (dirección) puede tomar uno de los siguientes valores:

  SPIN_STEP_FORWARD  # paso hacia adelante
  SPIN_STEP_BACKWARD # paso hacia atrás
  SPIN_PAGE_FORWARD  # página hacia adelante
  SPIN_PAGE_BACKWARD # página hacia atrás
  SPIN_HOME          # inicio
  SPIN_END           # fin
  SPIN_USER_DEFINED  # definido por el usuario

Este método comprime bastante funcionalidad, que explicaremos ahora. Muchos de estos parámetros usan valores del objeto Adjustment (Ajuste) que está asociado con un SpinButton (Botón Aumentar/Disminuir).

SPIN_STEP_FORWARD (paso adelante) y SPIN_STEP_BACKWARD (paso atrás) cambian el valor del SpinButton con una cantidad especificada por el increment (incremento), a menos que increment (incremento) sea igual a 0, en cuyo caso el valor se modifica con el step_increment (incremento de paso) del Adjustment.

SPIN_PAGE_FORWARD (página adelante) y SPIN_PAGE_BACKWARD (página atrás) simplemente alteran el valor del SpinButton por increment (incremento).

SPIN_HOME (inicio) pone el valor del SpinButton a la parte de abajo del rango del Adjustment .

SPIN_END (fin) fija el valor del SpinButton a la parte de arriba del rango del Adjustment .

SPIN_USER_DEFINED (definido por el usuario) simplemente modifica el valor del SpinButton con la cantidad especificada.

Ahora nos alejamos de los métodos para fijar y recuperar los atributos del rango de un SpinButton, y nos centramos en los métodos que modifican la apariencia y comportamiento del propio control SpinButton.

El primero de estos métodos se usa para limitar la caja de texto del SpinButton para que solo contenga un valor numérico. Esto evita que el usuario escriba cualquier otra cosa que no sea un valor numérico dentro de la caja de texto de un SpinButton:

  spin_button.set_numeric(numeric)

El argumento numeric es TRUE para limitar la caja de texto a valores numéricos o FALSE para quitar esta limitación.

Puedes fijar si quieres que el valor del SpinButton se quede en los valores inferior y superior del rango con el siguiente método:

  spin_button.set_wrap(wrap)

El SpinButton limitará los valores dentro del rango si wrap es TRUE.

Puedes hacer que el SpinButton redondee el valor al step_increment (incremento) más cercano, lo cual se fija dentro del objeto Adjustment usado en el SpinButton. Esto se consigue con el siguiente método cuando el argumento snap_to_ticks es TRUE :

  spin_button.set_snap_to_ticks(snap_to_ticks)

La política de actualización de un SpinButton se cambia con el siguiente método:

  spin_button.set_update_policy(policy)

Los valores posibles de esta política son:

  UPDATE_ALWAYS   # actualizar siempre

  UPDATE_IF_VALID # actualizar si es válido

Estas políticas afectan el comportamiento de un SpinButton cuando analiza el texto insertado y sincroniza su valor con los valores del Adjustment.

En el caso de UPDATE_IF_VALID (actualizar si es válido) el valor del SpinButton sólo cambia si el texto es un valor numérico que está dentro del rango especificado por el Adjustment. En cualquier otro caso el texto se resetea al valor actual.

En el caso de UPDATE_ALWAYS (actualizar siempre) se ignoran los errores de conversión al pasar el texto a un valor numérico.

Finalmente, se puede solicitar una actualización del SpinButton :

  spin_button.update()

El programa de ejemplo spinbutton.py muestra el uso de botones de aumentar/disminuir incluyendo el uso de varias características. La figura Figura 9.11, “Ejemplo de Botón Aumentar/Disminuir” muestra el resultado de ejecutar el programa de ejemplo:

Figura 9.11. Ejemplo de Botón Aumentar/Disminuir

Ejemplo de Botón Aumentar/Disminuir

El código fuente spinbutton.py es:

    1	#!/usr/bin/env python
    2	
    3	# ejemplo spinbutton.py
    4	
    5	import pygtk
    6	pygtk.require('2.0')
    7	import gtk
    8	
    9	class SpinButtonExample:
   10	    def toggle_snap(self, widget, spin):
   11	        spin.set_snap_to_ticks(widget.get_active())
   12	
   13	    def toggle_numeric(self, widget, spin):
   14	        spin.set_numeric(widget.get_active())
   15	
   16	    def change_digits(self, widget, spin, spin1):
   17	        spin1.set_digits(spin.get_value_as_int())
   18	
   19	    def get_value(self, widget, data, spin, spin2, label):
   20	        if data == 1:
   21	            buf = "%d" % spin.get_value_as_int()
   22	        else:
   23	            buf = "%0.*f" % (spin2.get_value_as_int(),
   24	                             spin.get_value())
   25	        label.set_text(buf)
   26	
   27	    def __init__(self):
   28	        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
   29	        window.connect("destroy", lambda w: gtk.main_quit())
   30	        window.set_title("Spin Button")
   31	
   32	        main_vbox = gtk.VBox(gtk.FALSE, 5)
   33	        main_vbox.set_border_width(10)
   34	        window.add(main_vbox)
   35	
   36	        frame = gtk.Frame("Not accelerated")
   37	        main_vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, 0)
   38	  
   39	        vbox = gtk.VBox(gtk.FALSE, 0)
   40	        vbox.set_border_width(5)
   41	        frame.add(vbox)
   42	
   43	        # Botones de aumentar/disminuir día, mes y año
   44	        hbox = gtk.HBox(gtk.FALSE, 0)
   45	        vbox.pack_start(hbox, gtk.TRUE, gtk.TRUE, 5)
   46	  
   47	        vbox2 = gtk.VBox(gtk.FALSE, 0)
   48	        hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
   49	
   50	        label = gtk.Label("Day :")
   51	        label.set_alignment(0, 0.5)
   52	        vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
   53	  
   54	        adj = gtk.Adjustment(1.0, 1.0, 31.0, 1.0, 5.0, 0.0)
   55	        spinner = gtk.SpinButton(adj, 0, 0)
   56	        spinner.set_wrap(gtk.TRUE)
   57	        vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)
   58	  
   59	        vbox2 = gtk.VBox(gtk.FALSE, 0)
   60	        hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
   61	  
   62	        label = gtk.Label("Month :")
   63	        label.set_alignment(0, 0.5)
   64	        vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
   65	
   66	        adj = gtk.Adjustment(1.0, 1.0, 12.0, 1.0, 5.0, 0.0)
   67	        spinner = gtk.SpinButton(adj, 0, 0)
   68	        spinner.set_wrap(gtk.TRUE)
   69	        vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)
   70	  
   71	        vbox2 = gtk.VBox(gtk.FALSE, 0)
   72	        hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
   73	  
   74	        label = gtk.Label("Year :")
   75	        label.set_alignment(0, 0.5)
   76	        vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
   77	  
   78	        adj = gtk.Adjustment(1998.0, 0.0, 2100.0, 1.0, 100.0, 0.0)
   79	        spinner = gtk.SpinButton(adj, 0, 0)
   80	        spinner.set_wrap(gtk.FALSE)
   81	        spinner.set_size_request(55, -1)
   82	        vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)
   83	  
   84	        frame = gtk.Frame("Accelerated")
   85	        main_vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, 0)
   86	  
   87	        vbox = gtk.VBox(gtk.FALSE, 0)
   88	        vbox.set_border_width(5)
   89	        frame.add(vbox)
   90	  
   91	        hbox = gtk.HBox(gtk.FALSE, 0)
   92	        vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)
   93	  
   94	        vbox2 = gtk.VBox(gtk.FALSE, 0)
   95	        hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
   96	  
   97	        label = gtk.Label("Value :")
   98	        label.set_alignment(0, 0.5)
   99	        vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
  100	  
  101	        adj = gtk.Adjustment(0.0, -10000.0, 10000.0, 0.5, 100.0, 0.0)
  102	        spinner1 = gtk.SpinButton(adj, 1.0, 2)
  103	        spinner1.set_wrap(gtk.TRUE)
  104	        spinner1.set_size_request(100, -1)
  105	        vbox2.pack_start(spinner1, gtk.FALSE, gtk.TRUE, 0)
  106	  
  107	        vbox2 = gtk.VBox(gtk.FALSE, 0)
  108	        hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
  109	  
  110	        label = gtk.Label("Digits :")
  111	        label.set_alignment(0, 0.5)
  112	        vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
  113	  
  114	        adj = gtk.Adjustment(2, 1, 5, 1, 1, 0)
  115	        spinner2 = gtk.SpinButton(adj, 0.0, 0)
  116	        spinner2.set_wrap(gtk.TRUE)
  117	        adj.connect("value_changed", self.change_digits, spinner2, spinner1)
  118	        vbox2.pack_start(spinner2, gtk.FALSE, gtk.TRUE, 0)
  119	  
  120	        hbox = gtk.HBox(gtk.FALSE, 0)
  121	        vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)
  122	
  123	        button = gtk.CheckButton("Snap to 0.5-ticks")
  124	        button.connect("clicked", self.toggle_snap, spinner1)
  125	        vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
  126	        button.set_active(gtk.TRUE)
  127	  
  128	        button = gtk.CheckButton("Numeric only input mode")
  129	        button.connect("clicked", self.toggle_numeric, spinner1)
  130	        vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
  131	        button.set_active(gtk.TRUE)
  132	  
  133	        val_label = gtk.Label("")
  134	  
  135	        hbox = gtk.HBox(gtk.FALSE, 0)
  136	        vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)
  137	        button = gtk.Button("Value as Int")
  138	        button.connect("clicked", self.get_value, 1, spinner1, spinner2,
  139	                       val_label)
  140	        hbox.pack_start(button, gtk.TRUE, gtk.TRUE, 5)
  141	  
  142	        button = gtk.Button("Value as Float")
  143	        button.connect("clicked", self.get_value, 2, spinner1, spinner2,
  144	                       val_label)
  145	        hbox.pack_start(button, gtk.TRUE, gtk.TRUE, 5)
  146	  
  147	        vbox.pack_start(val_label, gtk.TRUE, gtk.TRUE, 0)
  148	        val_label.set_text("0")
  149	  
  150	        hbox = gtk.HBox(gtk.FALSE, 0)
  151	        main_vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)
  152	  
  153	        button = gtk.Button("Close")
  154	        button.connect("clicked", lambda w: gtk.main_quit())
  155	        hbox.pack_start(button, gtk.TRUE, gtk.TRUE, 5)
  156	        window.show_all()
  157	
  158	def main():
  159	    gtk.main()
  160	    return 0
  161	
  162	if __name__ == "__main__":
  163	    SpinButtonExample()
  164	    main()