Précédent Index Suivant

Événements

La gestion des événements produits dans la fenêtre graphique permet de construire une interaction conviviale entre l'utilisateur et le programme. Les événements traités par Graphics sont l'appui d'une touche au clavier, le clic de la souris et le mouvement de celle-ci.

Le style de programmation change ainsi que l'organisation du programme. Celui-ci devient une boucle sans fin d'attente d'événements. À chaque nouveau déclenchement d'un événement, celui-ci est traité, puis le programme revient dans la boucle d'attente sauf lorsqu'un événement est prévu pour provoquer la sortie du programme.

Types et fonctions sur les événements

La fonction principale d'attente d'événements est wait_next_event de type event list -> status.

Les différents événements sont donnés par le type somme event.

type event = Button_down | Button_up | Key_pressed | Mouse_motion | Poll ;;
Les quatre premières valeurs correspondent, respectivement, à l'appui ou au relâchement d'un bouton de souris, au déplacement de la souris et à l'appui d'une touche. L'attente est bloquante, sauf si le constructeur Poll est ajouté à la liste des événements. Cette fonction retourne une valeur de type status :

type status =
{ mouse_x : int;
mouse_y : int;
button : bool;
keypressed : bool;
key : char};;


C'est un enregistrement contenant la position de la souris, un booléen indiquant si un bouton est pressé, un autre booléen pour le clavier et un caractère correspondant au caractère de la touche utilisée. Les fonctions suivantes exploitent les données de l'enregistrement des événements : La gestion d'événements proposée par Graphics est vraiment minimale pour la construction d'interfaces interactives. Néanmoins, le code est portable sur différents systèmes graphiques comme Windows, MacOS ou X-Windows. C'est d'ailleurs pour cela que cette bibliothèque ne tient pas compte des différents boutons de la souris. En effet les Mac n'en possèdent qu'un seul. Les autres événements : exposition et changement de taille de la fenêtre ne sont pas accessibles et sont laissés à la charge de la bibliothèque.

Squelette de programme

Tous les programmes d'interface utilisateur comprennent une boucle potentiellement infinie d'attente d'action de l'utilisateur. Une fois celle-ci transmise, le programme effectue le travail lié à cette action. La fonction suivante possède cinq paramètres fonctionnels. Les deux premiers servent à démarrer et à fermer l'application. Les deux suivants sont les fonctions de traitement des événements clavier et souris. Le dernier permet la gestion des exceptions lors des différents calculs de l'application. On suppose que les événements associés à la sortie de l'application déclenchent l'exception Fin.

# exception Fin;;
exception Fin
# let squel f_init f_end f_key f_mouse f_except =
f_init () ;
try
while true do
try
let s = Graphics.wait_next_event
[Graphics.Button_down; Graphics.Key_pressed]
in if s.Graphics.keypressed then f_key s.Graphics.key
else if s.Graphics.button
then f_mouse s.Graphics.mouse_x s.Graphics.mouse_y
with
Fin -> raise Fin
| e -> f_except e
done
with
Fin -> f_end () ;;
val squel :
(unit -> 'a) ->
(unit -> unit) ->
(char -> unit) -> (int -> int -> unit) -> (exn -> unit) -> unit = <fun>


Le squelette est utilisé pour programmer une mini-machine à écrire. L'appui d'une touche affiche le caractère tapé. Le clic d'une souris change le point courant. Le caractère '§' fait sortir du programme. La seule difficulté de ce programme est le saut de ligne. On suppose, pour simplifier, que la hauteur des caractères n'excède pas douze pixels.

# let next_line () =
let (x,y) = Graphics.current_point()
in if y>12 then Graphics.moveto 0 (y-12)
else Graphics.moveto 0 y ;;
val next_line : unit -> unit = <fun>
# let trait_char c = match c with
'§' -> raise Fin
| '\n' -> next_line ()
| '\r' -> next_line ()
| _ -> Graphics.draw_char c ;;
val trait_char : char -> unit = <fun>
# let go () = squel
(fun () -> Graphics.clear_graph () ;
Graphics.moveto 0 (Graphics.size_y() -12) )
(fun () -> Graphics.clear_graph())
trait_char
(fun x y -> Graphics.moveto x y)
(fun e -> ()) ;;
val go : unit -> unit = <fun>
Ce programme ne gère pas l'effacement d'un caractère par l'appui de la touche DEL.

Exemple : le télécran

Le télécran est ce petit jeu de dessin pour la coordination des mouvements. Un point apparaît sur une ardoise. Ce point peut se déplacer en X et en Y en utilisant deux boutons de contrôle de ces axes, sans jamais relever le crayon. On cherche à simuler ce comportement pour illustrer l'interaction entre un programme et un utilisateur. Pour cela on reprendra le squelette décrit précédemment. On utilisera certaines touches du clavier pour indiquer un mouvement sur un des axes.

On définit tout d'abord le type etat qui est un enregistrement décrivant la taille de l'ardoise en nombre de positions en X et en Y, la position courante du point, le facteur d'échelle pour la visualisation, la couleur du trait, la couleur du fond et la couleur pour repérer le point courant.

# type etat = {maxx:int; maxy:int; mutable x : int; mutable y :int;
scale:int;
bc : Graphics.color;
fc: Graphics.color; pc : Graphics.color} ;;


La fonction draw_point affiche un point en lui donnant comme paramètres les coordonnées, le facteur d'échelle et la couleur.

# let draw_point x y s c=
Graphics.set_color c;
Graphics.fill_rect (s*x) (s*y) s s ;;
val draw_point : int -> int -> int -> Graphics.color -> unit = <fun>


Toutes les fonctions d'initialisation, de gestion de l'interaction et de sortie du programme recevront un paramètre correspondant à l'état. Les quatre premières fonctions sont définies ci-après :

# let t_init e () =
Graphics.open_graph (":0 " ^ (string_of_int (e.scale*e.maxx)) ^
"x" ^ (string_of_int (e.scale*e.maxy))) ;
Graphics.set_color e.bc ;
Graphics.fill_rect 0 0 (e.scale*e.maxx+1) (e.scale*e.maxy+1) ;
draw_point e.x e.y e.scale e.pc ;;
val t_init : etat -> unit -> unit = <fun>
# let t_end e () =
Graphics.close_graph() ;
print_string "A bientôt..."; print_newline() ;;
val t_end : 'a -> unit -> unit = <fun>
# let t_mouse e x y = () ;;
val t_mouse : 'a -> 'b -> 'c -> unit = <fun>
# let t_except e ex = () ;;
val t_except : 'a -> 'b -> unit = <fun>


La fonction t_init ouvre la fenêtre graphique et affiche le point courant, t_end ferme cette fenêtre et affiche un message, t_mouse et t_except ne font rien. Le programme ne gérera pas les événements souris, ni les exceptions qui risquent de se déclencher à l'exécution. La fonction importante est celle de la gestion du clavier t_key :

# let t_key e c =
draw_point e.x e.y e.scale e.fc;
(match c with
'8' -> if e.y < e.maxy then e.y <- e.y + 1;
| '2' -> if e.y > 0 then e.y <- e.y - 1
| '4' -> if e.x > 0 then e.x <- e.x - 1
| '6' -> if e.x < e.maxx then e.x <- e.x + 1
| 'c' -> Graphics.set_color e.bc;
Graphics.fill_rect 0 0 (e.scale*e.maxx+1) (e.scale*e.maxy+1);
Graphics.clear_graph()
| 'f' -> raise Fin
| _ -> ());
draw_point e.x e.y e.scale e.pc;;
val t_key : etat -> char -> unit = <fun>


Elle affiche le point courant de la couleur du trait, et selon le caractère passé modifie, si possible, les coordonnées du point courant (caractères : '2', '4', '6', '8'), efface l'écran (caractère : 'c') ou déclenche l'exception Fin (caractère : 'f'), puis affiche le nouveau point courant. Les caractères autres que ceux mentionnés sont ignorés. Le choix des caractères de déplacement du curseur provient de la disposition du pavé numérique : les touches choisies correspondent aux chiffres indiqués et aux flèches de direction. Il est utile alors d'activer le pavé numérique pour l'ergonomie du programme.

On définit enfin un état et on applique la fonction squelette de la manière suivante :

# let etel = {maxx=120;maxy=120; x= 60; y= 60;
scale=4; bc=Graphics.rgb 130 130 130;
fc=Graphics.black; pc = Graphics.red};;
val etel : etat =
{maxx=120; maxy=120; x=60; y=60; scale=4; bc=8553090; fc=0; pc=16711680}
# let ardoise () =
squel (t_init etel) (t_end etel) (t_key etel)
(t_mouse etel) (t_except etel);;
val ardoise : unit -> unit = <fun>


L'appel à ardoise affiche la fenêtre graphique puis attend une interaction clavier. La figure 5.8 montre un dessin réalisé par ce programme.


Figure 5.8 : Le télécran



Précédent Index Suivant