Tutoriel Gtk2Hs

4.5 Dialogues, Stock Items et barres de progression

Un dialogue est un exemple de widget composé. Il se compose d’une fenêtre, d’une partie supérieure qui est une boite verticale et une zone d’action qui est une boite horizontale. Par défaut, les deux parties sont séparées par un séparateur.

La widget Dialog peut être utilisé pour des messages à l’intention de l’utilisateur et d’autres tâches similaires. Les fonctions de bases sont:

dialogNew :: IO Dialog

dialogRun :: DialogClass self => self -> IO ResponseID

Vous pouvez ajouter des boutons dans la zone d’action avec:

dialogAddButton :: DialogClass self => self -> String -> ResponseId -> IO Button

N’importe quel widget peut être ajouté d’une façon similaire avec dialogAddActionWidget.

La chaîne String dans dialogAddButton peut être le texte du bouton, mais dans la mesure où les dialogues sont principalement utilisés pour des situations standards, un StockItem sera généralement plus approprié.

StockItem sont des ressources qui sont reconnues dans tout Gtk2Hs, telles que les icônes standards IconSet. Vous pouvez définir votre propre stockitem mais plusieurs très utiles sont listés dans le module Graphics.UI.Gtk.General.StockItems. Ils ont l’identifiant StockId qui est un alias pour String. De cet identifiant, un widget (généralement un bouton) avec le texte de l’icône appropriée est automatiquement sélectionné.

Si vous utilisez un StockId en ajoutant un bouton à une boite de dialogue, vous pouvez également utiliser une constructeur ResponseId prédéfini avec ces boutons (ResponseId n’est pas de type String). Des réponses adaptées peuvent être construites avec ResponseUser Int.

A tout moment, quand un bouton dialogue est pressé, sa réponse est passé à la l’application qui l’a appelé par dialogRun. D’après la documentation de Gtk2Hs dialogRun fige la boule principale jusqu’à ce que le dialogue émette le signal de réponse ou qu’il soit détruit.

Les barres de progression sont utilisées pour montrer le statut d’une opération en cours de déroulement.

progressBarNew :: IO ProgressBar

Bien qu’il n’y ai qu’un seul type, il y a deux façons distinctes d’utiliser une barre de progression. Si on connaît l’avancement de la tâche, la fraction (entre 0.0 inclus et 1.0 inclus) peut être fixée avec:

progressBarSetFraction :: ProgressBarClass self => self -> Double -> IO ()

Cela entraine le remplissage de la barre de progression avec la quantité spécifiée (entre 0.0 et 1.0). Pour représenter la progression, cette fonction doit être appelée régulièrement pendant l’opération.

Lorsque l’on ne connaît pas le travail déjà accompli, la barre peut être bougée d’avant en arrière avec:

progressBarPulse :: ProgressBarClass self => self -> IO ()

Cette fonction doit aussi être appelée de façon répétée pour montrer l’activité en cours. Il y a plusieurs autres fonctions pour contrôler l’affichage d’une barre de progression, comme l’orientation, le texte, etc… elles sont assez faciles.

La mise en œuvre, en revanche, n’est pas simple car les barres de progression sont généralement utilisées avec des temporisations ou des fonctions similaires pour donner l’impression du multitâche. Avec Haskell, vous pouvez utilisez les threads et les communications pour communiquer entre les threads.

Dans l’exemple qui suit, on simule une activité en utilisant timeoutAdd, qui lance une fonction régulièrement à un intervalle de temps défini en millisecondes. La fonction est passée à timeoutAdd et doit retourner un type IO Bool. Lorsque la fonction renvoie True, le temporisateur est lancé à nouveau. Quand elle renvoie False, il est stoppé. La priorité de timeoutAdd est priorityDefault et est du type Priority

timeoutAdd :: IO Bool -> Int -> IO HandlerId

Dans l’exemple, nous définissons la fonction showPulse, qui entraîne la pulsation de la barre de progression et retourne toujours IO True. Le pas de la pulsation, la quantité que l’indicateur bouge dans la barre, est fixé à 1.0 avec progressBarSetPulseStep.

Gtk Gtk2Hs Progress bar pulsing

L’exemple est un peu atypique dans l’utilisation d’un dialogue dans la mesure où on le garde pour montrer la barre de progression après que l’utilisateur ait pressé le bouton apply. Pour fermer l’application, le dialogue doit être détruit en détruisant la fenêtre. Les boutons close et cancel ne fonctionnent plus après que le bouton apply ait été sélectionné. Si ils sont sélectionnés d’abord au lieu de Apply, l’application se fermera.

Si le widget dialogue est détruit, mainQuit est appelée. Comme montré ci-dessus, un Dialog est constitué d’une fenêtre et de deux boites. Les boites sont modifiées par des fonctions spéciales et la barre de progression est empaquetée dans la partie supérieure en utilisant dialogGetUpper. Les boutons dans un dialogue sont visibles par défaut, mais les widgets dans la partie supérieure ne le sont pas. Un dialogue est une instance de WindowClass, et on peut donc définir le titre, la longueur et la hauteur par défaut si on le souhaite.

Quelque chose d’important à noter: Un widget est visible seulement si son parent est visible. Donc, pour afficher la barre de progression, on utilise widgetShowAll sur la boite verticale et pas widgetShow sur la barre de progression.

import Graphics.UI.Gtk

main :: IO ()
main = do
  initGUI

  dia <- dialogNew
  set dia [windowTitle := "Time Flies"]
  dialogAddButton dia stockApply  ResponseApply
  dialogAddButton dia stockCancel ResponseCancel
  dialogAddButton dia stockClose  ResponseClose

  pr <- progressBarNew
  progressBarSetPulseStep pr 1.0

  upbox <- dialogGetUpper dia
  boxPackStart upbox pr PackGrow 10
  widgetShowAll upbox

  answer <- dialogRun dia
  if answer == ResponseApply 
     then do tmhandle <- timeoutAdd (showPulse pr) 500
             return ()
     else widgetDestroy dia

  onDestroy dia mainQuit
  mainGUI

showPulse :: ProgressBar -> IO Bool
showPulse b = do progressBarPulse b
                 return True