Programmation événementielle et composants dinterface (« widgets »)
Logiciel traditionnel, sans interface utilisateur { Lire les entrées dun fichier Effectuer des calculs Écrire les sorties dans un fichier }
… avec interface utilisateur minimale { Lire les entrées du clavier Effectuer des calculs Écrire les sorties sur lécran }
… dans une boucle Répéter { Afficher linvite de commandes Lire la commande Exécuter la commande Écrire les sorties sur lécran }
Logiciel événementiel (« event-driven program») Répéter { Attendre un événement Traiter lévénement }
Logiciel événementiel pour une interface graphique Afficher linterface (fenêtre(s), etc.) Répéter { Attendre un événement dentrée e switch(e.type) { case KEY_PRESS:... case MOUSE_BUTTON:... case MOUSE_MOTION:... } Si nécessaire, mettre à jour laffichage } Boucle dévénements
Logiciel traditionnel: Le logiciel « a le contrôle ». Le logiciel peut prendre le temps nécessaire pour traiter les entrées. Lutilisateur doit attendre. Les entrées sont limitées aux chaînes de caractères. Logiciel graphique interactif: Lutilisateur doit (avoir limpression de) « avoir le contrôle ». Le logiciel doit traiter chaque événement assez rapidement pour répondre en temps réel. Lutilisateur peut générer différents événements à tout moment.
Structure, ou classe, dévénement Event { unsigned long int timestamp; int type; // KEY_PRESS, MOUSE_... int x, y; int key; boolean flag }
Sortes dévénements (1) KEY_DOWN ou …_PRESS (touche appuyée), KEY_UP ou …_RELEASE (touche relâchée), KEY_TYPED (synonyme pour DOWN + UP) – Attributs: temps, caractère ASCII (a, b, c, …), touche de clavier (F1, F2, PageUp, Esc, …), sil sagit dune auto-répétition ou non, coordonnées (x, y) de la souris, état des touches de modification (Ctrl, Shift, etc.)
Sortes dévénements (2) MOUSE_DOWN ou …_PRESS (bouton appuyé), MOUSE_UP ou …_RELEASE (bouton relâché), MOUSE_CLICK (synonyme pour DOWN + UP) MOUSE_DOUBLE_CLICK, MOUSE_MOTION ou …_MOVEMENT (mouvement), MOUSE_DRAG (glissement) – Attributs: temps, bouton (1-5) (gauche, droit, milieu, …), coordonnées (x, y), état de tous les boutons (utile pour les combinaisons), état des touches de modification (Ctrl, Shift, etc.) – Taux déchantillonnage de la position dépend habituellement de la charge CPU
Autres sortes dévénements?
Sortes dévénements (3) RESIZE (redimensionnement), MAXIMIZE, MINIMIZE – Attributs: temps, nouvelles dimensions (width, height) WINDOW_CLOSE – Permet au logiciel de sauvegarder ses données, faire le ménage, etc.
Autres sortes dévénements?
Sortes dévénements (4) MOUSE_ENTER (souris entre dans la fenêtre), MOUSE_LEAVE ou …_EXIT (souris sort de la fenêtre) – Attributs: semblable à ceux des autres événements de souris REDRAW ou EXPOSE ou PAINT (demande de redessiner) – Peut être généré par un redimensionnement, un mouvement dune autre fenêtre, ou par une demande du client même – Attributs: rectangle(s) à redessiner (x, y, width, height), c.-à-d. les rectangles endommagés (« damaged »)
Rectangles endommagés
Boucle dévénements en C avec Xlib (UNIX/X11) XEvent event; ouvrir une fenêtre while (!quit) { XNextEvent(..., &event); // attend le prochain événement switch (event.type) { case Expose:... // redessiner case ConfigureNotify:... // redimensionnement case ButtonPress:... // bouton de souris case ButtonRelease:... // bouton de souris case MotionNotify:... // mouvement de souris case KeyPress: // touche de clavier switch (XLookupKeysym(&event.xkey,...)) { case XK_F1:... // afficher de laide case XK_Escape: quit=true; break; } break; }
Boucle dévénements Xlib – comment travailler en même temps while (!quit) { while (0 == XEventsQueued(...)) { // Peut avancer une animation, simulation, des calculs, etc. faireUnPeuDeTravail(); usleep(100000); // dormir une fraction de seconde } XNextEvent(..., &event); switch (event.type) { case... } Optionnel Une autre façon de faire du travaille en même temps: utiliser des événements spéciaux …
Sortes dévénements (5) TIMER ou TIMEOUT (un délai de temps expiré), IDLE (le CPU est libre) – Envoyés au client sur demande seulement – Permettent de faire un peu de travail en attendant dautres événements – Les « timeouts » peuvent être envoyés de façon périodique (exemple: pour une animation), ou seulement une fois (ex: pour retarder une action)
Exemples dactions retardées Affichage dune infobulleOuverture dun sous-menu
Traitement dévénements en C avec GLUT (OpenGL) void drawHandler() {... // redessiner } void reshapeHandler( int width, int height ) {... // redimensionnement } void mouseHandler( int button,int state,int x,int y ) { // boutons de souris if (button==GLUT_LEFT_BUTTON && state==GLUT_DOWN)... } void passiveMotionHandler( int x, int y ) {... // mouvement de souris } void motionHandler( int x, int y ) {... // glissement de souris } void keyboardHandler( unsigned char key, int x, int y ) {... // touches normales } void specialKeyHandler( int key, int x, int y ) {... // touches spéciales } int main(... ) { glutInit... glutCreateWindow( "My Window" ); glutDisplayFunc( drawHandler ); glutReshapeFunc( reshapeHandler ); glutMouseFunc( mouseHandler );... glutMainLoop(); } Ces sous-routines sappellent des « event handlers » (traiteurs dévénements) ou « callbacks » Où est la boucle dévénements?
Classes dévénements, architecture orientée objets Event KeyEventMouseEvent MouseButtonEventMouseMotionEvent … …
Les informations décrivant les événements peuvent être encodées dans une structure (exemple: Xlib), une ou plusieurs classes (ex: Java), ou de simples paramètres (ex: GLUT). Ces informations peuvent être traitées par une seule sous-routine, ou par plusieurs sous-routines selon le type dévénement. (Ces sous-routines sappellent « event handlers » (traiteurs dévénements), ou « callbacks », ou sont des méthodes dobjets « event listeners » (écouters dévénements)) La boucle dévénements peut être située dans le code du client (ex: Xlib), ou à lextérieur (dans une librarie ou « framework »; ex: GLUT).
Quelques exemples en Java …
Quelques éléments des interfaces graphiques
Les fenêtres
Curseurs standards de X11 Les curseurs de souris (ou pointeurs)
Curseur de souris, ou pointeur « hotspot »
Les curseurs de texte (« caret »)
Les widgets
Les boutons
Les cases à cocher (« checkbox ») Pour des choix binaires et indépendants
Les boutons radio Pour des choix mutuellement exclusifs
Les infobulles (« tooltips »)
Barre de défilement ou glisseur (« scrollbar » ou « slider ») Défileur (« thumb » ou « slider »)
Barre de défilement contextuelle (« popup slider »)
Barre de progrès
Les champs Champs de texte Champs numériques – Avec « spinner » – Avec unités – Avec barre de défilement
Les menus item ou commande (« menu item »)
Barre de menus
Menu déroulant (« Pull-down menu »)
Menu contextuel (« popup menu » ou « context-sensitive menu »)
Cest quoi la différence entre « popup » et « context-sensitive » ?
Les menus doptions (« option menus »)
Affichage de sous-menus
Les ellipses …
Menus détachés ou menus flottants (« Tear-off menus »)
Liste déroulante (« Pick list » ou « Scrolling list »)
Menu divisé (« Split menu »)
Les cadres
Les onglets
Des onglets hiérarchiques ?
Les séparateurs
Un bouton + menu
Créateur de tables
Les palettes de boutons
Les barres doutils
Des boîtes de dialogue
2 sortes de boîtes de dialogue Modale (« modal ») – Oblige lutilisateur de compléter son interaction avec la boîte avant de retourner à la fenêtre principale Non-modale (« modeless ») – Plusieurs boîtes non-modales peuvent être ouvertes en même temps dans une même application
Les tableaux
« tree list »
Cadres défilables (« scrolling area »)
Sélecteur de couleur (« color picker »)
Observations Widgets simples vs widgets composés Widgets composés: – Un enfant vs plusieurs enfants – Contenu fixe vs contenu variable
Observations (2) Certains widgets aident à gérer lespace Onglets, menus, barres de défilement, …
Lexemple de SimpleDraw.java …
class Stroke ArrayList points = …; AlignedRectangle2D getBoundingRectangle() draw() class Drawing ArrayList strokes = …; AlignedRectangle2D getBoundingRectangle() draw() class MyCanvas RadialMenuWidget radialMenu = …; paintComponent() mousePressed() mouseReleased() mouseMoved() mouseDragged() class SimpleDraw actionPerformed() createUI() main() class CustomWidget pressEvent() releaseEvent() moveEvent() dragEvent() draw() class RadialMenuWidget … RadialMenuWidget.java SimpleDraw.java CustomWidget.java
JMenuBar menuBar JMenu("File") JMenu("View") JMenu("Help") JMenuItem("Clear") JMenuItem(Quit") JFrame frame Container toolPanelMyCanvas canvas JRadioButton(…) JButton("Delete Selection") JButton("Frame Selection") … … RadialMenuWidget