El widget SpinButton se usa normalmente para permitir al usuario
seleccionar un valor dentro de un rango de valores numéricos. Consta de una caja
de entrada de texto con dos botones flecha (arriba y abajo) enganchados en el
lateral. Seleccionando alguna de las flechas el valor se modifica subiendo
o bajando dentro del rango especificado.
La caja de entrada de texto también puede ser editada directamente para introducir
un valor.
SpinButton es una instancia de EditableClass, por lo que los
atributos y funciones definidas allí también están disponibles.
Los botones aumentar/disminuir permiten definir un valor con cero o 'n' decimales. El incremento, (el paso), se define por el programador. Si mantenemos pulsado uno de los botones se puede originar una aceleración en el cambio del valor, en función del tiempo en que esté pulsado.
SpinButton usa un objeto de Adjustment para almacenar
la información sobre el rango de valores que puede tomar. Recuerda que el widget
Adjustment se crea con la siguiente función:
adjustmentNew :: Double -- valor - El valor inicial del rango
-> Double -- mínimo - Valor mínimo del rango
-> Double -- máximo - Valor máximo del rango
-> Double -- incrementoPaso - El menor de dos posibles incrementos
-> Double -- incrementoPágina - El mayor de dos posibles incrementos
-> Double -- tamañoPágina - El tamaño del área visible
-> IO Adjustment
Los atributos del objeto Adjustment asociado al SpinButton
tienen el siguiente uso:
SpinButtonAdemás, el pusador 3 del ratón se puede usar para ir directamente a los valores máximo y mínimo al seleccionar alguno de los botones. Ten en cuenta que esto puede depender de la configuración del ratón en tu sistema.
Vamos a ver como podemos crear un botón aumentar/disminuir:
spinButtonNew :: Adjustment -> Double -> Int -> IO SpinButton
El segundo argumento (climbRate) (tasa de escalado) es un valor entre 0.0 y 1.0 e indica la velocidad de cambio del botón aumentar/disminuir cuando se pulsa una tecla. El tercer argumento indica el número de posiciones decimales con que será mostrado el valor.
También hay un constructor que permite crear un botón aumentar/disminuir sin necesidad de crear un adjustment.
spinButtonNewWithRange :: Double -> Double -> Double -> IO SpinButton
Los tres argumentos, todos de tipo Double, corresponden al valor
mínimo posible, al máximo, y al incremento añadido o sustraído al pulsar
los botones del widget.
Una vez creado, el SpinButton se puede reconfigurar mediante la siguiente función:
spinButtonConfigure :: SpinButtonClass self => self -. Adjustment -> Double -> Int
El primer argumento indica el widget SpinButton que debe ser
reconfigurado. los otros argumentos son, el climbRate y el número
de decimales a mostrar.
Los atributos del SpinButton que pueden ser modificados o consultados
con las funciones genéricas get y set son:
spinButtonAdjustment :: SpinButtonClass self => Attr self Adjustment spinButtonClimbRate :: SpinButtonClass self => Attr self Double spinButtonDigits :: SpinButtonClass self => Attr self Int spinButtonSnapToTicks :: SpinButtonClass self => Attr self Bool spinButtonNumeric :: SpinButtonClass self => Attr self Bool spinButtonWrap :: SpinButtonClass self => Attr self Bool spinButtonValue :: SpinButtonClass self => Attr self Double
Los primeros tres valores ya los hemos analizado. El atributo
spinButtonSnapToTicks determina si los valores erróneos
se cambian automáticamente a valores válidos del botón aumentar/disminuir (por defecto es
False). El attributo spinButtonNumeric determina si
los valores no-numéricos deben ser ignorados (por defecto es False), y
spinButtonWrap se usa si un botón aumentar/disminuir debe funcionar de una manera
circular. (por defecto es False).
El atributo spinButtonValue se usa para leer el valor actual o establecer un
nuevo valor (por defecto es 0).
También puedes usar:
spinButtonSpin :: SpinButtonClass self => self -> SpinType -> Double -> IO ()
donde SpinType determina el tipo de cambio y
Double (incremento) determina el valor.
SpinType tiene los siguientes constructores:
SpinStepForward Paso adelanteSpinStepBackward Paso atrásSpinPageForward Página adelanteSpinPageBackward Página atrásSpinHome InicioSpinEnd FinSpinUserDefined Definido por usuario
muchos de estos constructores usan los valores del objeto Adjustment que está asociado con
el botón aumentar/disminuir. SpinStepForward y SpinStepBackward
cambian el valor del botón aumentar/disminuir en la cantidad indicada por el incremento, a no ser
que valga 0, en cuyo caso el valor será cambiado por el stepIncrement del ajuste.
SpinPageForward y SpinPageBackward cambian el valor del
SpinButton en el incremento.
SpinPageHome y SpinPageEnd establecen el valor mínimo
y máximo respectivamente del rango del Adjustment.
SpinUserDefined modifica el valor del botón aumentar/disminuir en la cantidad
especificada.
Los botones aumentar/disminuir tienen una política de actualizaciones:
spinButtonUpdatePolicy :: SpinButtonClass self => Attr self SpinButtonUpdatePolicy
Los constructores de SpinButtonUpdatePolicy pueden ser
UdateAlways o UpdateIfValid. Estas políticas afectan
al comportamiento del SpinButton al analizar (procesar) el texto
insertado y sincronizar su valor con los valores del Adjustment. En el
caso de UpdateIfValid (actualiza si es válido) el valor del botón aumentar/disminuir sólo
se cambia si la entrada de texto es un valor numérico dentro del rango especificado en
el Adjustment. Si no es así se mantiene el valor actual. En caso de
UpdateAlways ignoramos los errores de conversión del "texto" en un valor numérico.
Finalmente, puedes solicitar que el propio SpinButton se actualice:
spinButtonUpdate :: SpinButtonClass self => self -> IO ()
Es el momento de volver a los ejemplos. Esta es una imagen después de juguetear con algunos de los atributos:

Todos los botones aumentar/disminuir han sido creados con la siguiente función. Usa la
función spinButtonNewWithRange. Como el stepIncrement será 1.0
en todos los casos, no es un parámetro de la función myAddSpinButton.
myAddSpinButton :: HBox -> String -> Double -> Double -> IO SpinButton
myAddSpinButton box name min max = do
vbox <- vBoxNew False 0
boxPackStart box vbox PackRepel 0
label <- labelNew (Just name)
miscSetAlignment label 0.0 0.5
boxPackStart vbox label PackNatural 0
spinb <- spinButtonNewWithRange min max 1.0
boxPackStart vbox spinb PackNatural 0
return spinb
En la función main usamos uno de los botones aumentar/disminuir existentes,
pero le damos un nuevo adjustment con spinButtonConfigure. Los
límites antiguos de -1000.0 y 1000.0 se reemplazan por -100.0 y 100.0. Fíjate en los
paréntesis que rodean a los números negativos. El valor inicial se establece en 0.0 y
el incremento de paso es de 0.25. El incremento de página, que es el que consigues al
pulsar el segundo botón del ratón en la flecha del botón aumentar/disminuir, se
establece en 10.0. El tamaño de página, que no se usa, es 0 aquí. Pulsando el tercer botón
del ratón en una de las flechas, salta hacia el límite correspondiente, -100.0 o 100.0.
Aquí, la nueva señal es onValueSpinned, que se emite siempre que el usuario
cambie el valor de un botón aumentar/disminuir. Aquí se usa para controlar el número de
dígitos decimales mostrados en el botón aumentar/disminuir spinLarge.
Fíjate en el redondeo del valor, necesario para convertir el tipo Double a un
tipo Integral.
En el ejemplo usamos las funciones genéricas get y set
en los atributos (hay funciones específicas para ello). Es un estilo de programación
recomendado en Gtk2Hs, ya que en el futuro las funciones específicas serán eliminadas.
import Graphics.UI.Gtk
main:: IO ()
main = do
initGUI
window <- windowNew
mainbox <- vBoxNew False 0
set window [windowTitle := "Spin buttons", containerBorderWidth := 10,
windowDefaultWidth := 250, windowDefaultHeight := 200,
containerChild := mainbox]
hbox1 <- hBoxNew False 0
frame1 <- frameNew
set frame1 [frameLabel := "Simple SpinButtons", containerChild := hbox1,
frameLabelYAlign := 0.8, frameShadowType := ShadowOut]
boxPackStart mainbox frame1 PackNatural 5
spinD <- myAddSpinButton hbox1 "Day:" 1.0 31.0
spinM <- myAddSpinButton hbox1 "Month:" 1.0 12.0
spinY <- myAddSpinButton hbox1 "Year:" 2000.0 2100.0
set spinY [spinButtonValue := 2007]
vbox1 <- vBoxNew False 5
frame2 <- frameNew
set frame2 [frameLabel := "More Features", containerChild := vbox1,
frameLabelYAlign := 0.8, frameShadowType:= ShadowOut]
boxPackStart mainbox frame2 PackNatural 5
hbox2 <- hBoxNew False 0
boxPackStart vbox1 hbox2 PackNatural 0
spinLarge <- myAddSpinButton hbox2 "Value:" (-1000.0) 1000.0
adj <- adjustmentNew 0.0 (-100.0) 100.0 0.25 10.0 0.0
spinButtonConfigure spinLarge adj 0.0 2
spnctl <- myAddSpinButton hbox2 "Decimal:" 0.0 10.0
set spnctl [spinButtonValue := 2.0]
tsnap <- checkButtonNewWithLabel "Snap to 0.25-ticks"
boxPackStart vbox1 tsnap PackNatural 0
tnumr <- checkButtonNewWithLabel "Numeric only input mode"
boxPackStart vbox1 tnumr PackNatural 0
twrap <- checkButtonNewWithLabel "Wraparound at limits"
boxPackStart vbox1 twrap PackNatural 0
widgetShowAll window
onValueSpinned spnctl $ do newdig <- get spnctl spinButtonValue
set spinLarge [spinButtonDigits := (round newdig)]
onToggled tsnap $ do st <- get tsnap toggleButtonActive
set spinLarge [spinButtonSnapToTicks := st]
onToggled tnumr $ do st <- get tnumr toggleButtonActive
set spinLarge [spinButtonNumeric := st]
onToggled twrap $ do st <- get twrap toggleButtonActive
set spinLarge [spinButtonWrap := st]
onDestroy window mainQuit
mainGUI