Tutoriel Gtk2Hs

4.7 Boutons compteurs

Le widget SpinButton est généralement utilisé pour permettre à l’utilisateur de choisir une valeur dans une plage de données. Il comprend une boite de saisie de texte avec deux boutons fléchés en haut et en bas sur le coté. Cliquer sur un des boutons entraîne l’incrément ou le décrément dans la plage de valeurs possibles. La boite de saisie peut également être éditée directement pour renseigner une valeur spécifique. SpinButton est une instance de EditableClass, les fonctions et attributs qui sont associés à cette classe sont donc disponibles.

Le bouton compteur permet de définir le nombre de décimales et les incréments. Le fait d’appuyer de façon prolongée sur les boutons entraîne une accélération du changement des valeurs en fonction de la durée de pression du bouton.

SpinButton utilise un objet Adjustment pour conserver les informations des valeurs et de la page que le bouton compteur peut prendre. Rappelons qu’un widget Adjustment se crée avec la fonction suivante:

adjustmentNew :: Double        -- value
              -> Double        -- lower
              -> Double        -- upper
              -> Double        -- stepIncrement
              -> Double        -- pageIncrement
              -> Double        -- pageSize
              -> IO Adjustment

Ces attributs issus de Adjustment sont utilisés par le SpinButton de la façon suivante

  • value — La valeur initiale du SpinButton
  • lower — La valeur minimale
  • upper — La valeur maximale
  • stepIncrement — La valeur qui doit être incrémenté ou décrémenté quand le bouton 1 de la souris est pressé.
  • pageIncrement — La valeur qui doit être incrémenté ou décrémenté quand le bouton 2 de la souris est pressé.
  • pageSize — Inusité

En complément, le bouton 3 de la souris peut être utilisé pour aller directement aux valeurs maximum ou minimum en cliquant sur un des boutons.

Voyons maintenant comment créer un bouton compteur:

spinButtonNew :: Adjustment -> Double -> Int -> IO SpinButton

Le deuxième argument (climbRate) prend une valeur entre 0.0 et 1.0 et indique à quelle vitesse le bouton compteur est modifié quand une flèche est cliquée. Le troisième argument spécifie le nombre d’emplacements dans lesquels la valeur doit être affichée.

Il y a aussi un constructeur qui permet la création d’un bouton compteur sans avoir à créer manuellement un Adjustment.

spinButtonNewWithRange :: Double -> Double -> Double -> IO SpinButton

Les trois arguments, tous de type Double spécifient respectivement la valeur minimum, la valeur maximum, l’incrément ajouté ou soustrait par le widget compteur.

Un SpinButton peut être reconfigure après la création en utilisant la fonction suivante:

spinButtonConfigure :: SpinButtonClass self => self -. Adjustment -> Double -> Int

Le premier argument le SpinButton qui doit être reconfiguré, les autres arguments sont le climbRate et le nombre de décimales à afficher.

Les attributs du SpinButton qui peuvent être récupérés et changés avec les fonctions génériques get et set sont:

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

Les trois premières ont été abordées auparavant. L’attribut spinButtonSnapToTicks détermine si les valeurs erronées sont automatiquement changées vers l’incrément le plus proche (False par défaut). L’attribut spinButtonNumeric détermine si les caractères non-numériques doivent être ignorés (False par défaut) et spinButtonWrap détermine si le bouton compteur doit reboucher sur la plage de valeur lorsque les limites sont dépassées (False par défaut).

L’attribut spinButtonValue est utilisé pour lire la valeur courante ou définir une nouvelle valeur (par défaut 0).

Pour changer la valeur d’un bouton compteur, on peut aussi utiliser:

spinButtonSpin :: SpinButtonClass self => self -> SpinType -> Double -> IO ()

ou SpinType détermine le type de changement et Double détermine la valeur.

SpinType possède les constructeur suivant:

  • SpinStepForward
  • SpinStepBackward
  • SpinPageForward
  • SpinPageBackward
  • SpinHome
  • SpinEnd
  • SpinUserDefined

Beaucoup de ces réglages utilisent des valeurs de l’objet Adjustment qui est associé au bouton compteur. SpinStepForward et SpinStepBackward changent la valeur du bouton compteur du nombre d’incréments spécifié, à moins qu’il soit égal à 0, auquel cas, la valeur est remplacée par la valeur de stepIncrement. SpinPageForward et SpinPageBackward modifient simplement la valeur du SpinButton par l’incrément. SpinPageHome et SpinPageEnd mettent la valeur respectivement au maximum ou au minimum de la plage de Adjustment. SpinUserDefined modifie la valeur du bouton compteur par la valeur spécifiée.

Un bouton compteur a également une politique de mise à jour:

spinButtonUpdatePolicy :: SpinButtonClass self => Attr self SpinButtonUpdatePolicy

Les constructeurs de SpinButtonUpdatePolicy sont soit UdateAlways ou UpdateIfValid. Ces politiques affectent le comportement d’un SpinButton lorsque qu’un texte est inséré et que sa valeur est synchronisée avec celle de Adjustment. Dans le cas de UpdateIfValid, le bouton compteur est changé seulement si le texte rentré est une valeur numérique dans la plage spécifiée par Adjustment. Dans le cas ou cela n’est pas vrai, le texte est réinitialisé à la valeur courante. Dans le cas de UpdateAlways, les erreurs sont ignorées pendant la conversion du texte en valeur numérique.

Au final, vous pouvez demander explicitement qu’un SpinButton se mette à jour lui-même:

spinButtonUpdate :: SpinButtonClass self => self -> IO ()

On peut maintenant prendre un nouvel exemple. Voici une capture d’écran après avoir modifié certains réglages:

Gtk Gtk2Hs Spin button example

Les boutons compteurs ont tous été créés avec la fonction suivante qui utilise spinButtonNewWithRange.stepIncrement est toujours fixé à 1.0 et n’est donc pas un paramètre de 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

Dans la fonction main, on utilise un des boutons compteurs qui existe déjà mais en lui donnant un nouveau paramétrage avec spinButtonConfigure. L’ancienne limite de -1000.0 à 1000.0 est dorénavant remplacée par -100.0 et 100.0 (Notez les parenthèses autour des valeurs négatives). La valeur d’origine est définie à 0.0 et l’incrément à 0.25. L’incrément de page est défini à 10.0. La taille de page (qui n’est pas utilisée ici) est fixée à 0.0.

Le nouveau signal ici est onValueSpinned, qui est émis quand l’utilisateur change la valeur du bouton compteur. Ici, il est utilisé pour contrôler le nombre de décimales qui sont affichées dans le bouton spinLarge. Notez qu’il est nécessaire d’arrondir la valeur pour convertir le Double en en type Integral (entier).

Dans cet exemple, nous avons utilisé les fonctions get et set sur les attributs plutôt que les fonctions spécifiques qui sont également disponibles. C’est la façon de programmer qu’il est recommander d’utiliser avec Gtk2Hs car dans l’avenir certaines fonctions risqueront d’être obsolètes.

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