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:
SpinButton
Ademá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