Cours 2a : Programmation des interfaces graphiques [email protected] (partie de la présentation basée sur des transparents d’Anastasia Bezerianos, Fanis Tsandilas et Michel Beaudouin-Lafon) Interfaces graphiques (GUIs) l'interaction graphique : les entrées sont spécifiées directement à partir des sorties. Le périphérique d’entrée spécifie une commande de position à l'écran qui désigne un objet précédemment affiché par le système (pointage). Elle est familière dans le monde physique. Interfaces WIMP WIMP : Window, Icons, Menus and Pointing Fenêtres, Icones, menus et pointage. Présentation Fenêtre, icones et autres objets graphiques Interaction Menus, boites de dialogue, texte Entrée pointage, sélection, tracé Boucle action perception Feedback Couches logicielles Squelette d’application MacApp Java Swing, Qt, GTK+, MFC, Cocoa X Windows GDI+, Quartz, GTK+/Xlib, OpenGL Windows, Mac OS X, Linux Dispositifs d’entrée/sortie Entrée : Saisir des commandes et des données Sortie : le système affiche de l’information et montre son état Interactivité vs. Algorithmique système algorithmique (fermés) : – lit des entrées, calcule, produit un résultat – il y a un état final système interactif (ouverts) : – évènements provenant de l’extérieur – boucle infinie, non déterministe Problème vous avez appris à programmer des algorithmes (la partie “calcul”) la plupart des langages de programmation (C, C ++, Java, Lisp, Scheme, Ada, Pascal, Fortran, Cobol, ...) sont conçus pour écrire des algorithmes, pas des systèmes interactifs Problème Traitement des entrées/sorties pendant le calcul – Instructions de sortie (print, put, send,…) pour envoyer des données aux périphériques – Instructions de lecture (read, get, receive, …) pour lire l’état ou changement d’états de périphériques d’entrée, du façon bloquante Problème obligés d’écrire des SI sous une forme algorithmique : Comment gérer les entrées - 1 pér. à la fois - Attente active - état des plusieurs pér. - CPU Programmation évènementielle File d’attente (queue) queue.enqueue(event) while active if queue is not empty event <- queue.dequeue() source <- findSource(event) source.processEvent(event) end if end while Programmation évènementielle Source : Mouse Click File d’attente (queue) queue.enqueue(event) Cible : Bouton « Cancel » while active if queue is not empty event <- queue.dequeue() source <- findSource(event) source.processEvent(event) end if end while processEvent(event) target <- FindTarget (event) if (target ≠ NULL) target.processEvent(event) Exemple : Swing (AWT) 3 processus • Initial : main() • EDT gères la liste d’évènements : envoie les évènements aux listeners et appel la méthode paint • Tache de fond : exécute les actions plus longues Boîte à outils d’interface Bibliothèques d’objets interactifs (les « widgets ») que l’on assemble pour construire l’interface Fonctionnalités pour faciliter la programmation d’applications graphiques interactives Boîte à outils d’interface Pourquoi Java Swing Java (toutes plateformes et beaucoup de librairies) Beaucoup de ressources en ligne Pourquoi Java Swing Java (toutes plateformes et beaucoup de librairies) Beaucoup de ressources en ligne Alternatives • JavaFX : s’impose petit à petit pour les interfaces en Java car permet de travailler avec beaucoup de matériel différent. les « widgets » bouton menu fenêtre barre d’outils onglet étiquette zone de texte bouton radio liste barre de défilement « slider » les widgets de Swing les widgets de Swing Complexité des widgets widgets « simples » buttons, barres de défilement, … widgets « composés » Destinés à contenir d’autres widgets (simples ou composés) Boites de dialogue, menus, … Hiérarchie des widgets – un composant ne peut appartenir qu’à un seul « container » Racine (composé) correspond à une fenêtre de l’appli Nœuds (composé) Structure visuel ou fonctionnel du contenu Feuille (simple) avec lesquels l’utilisateur peut interagir Classes des widgets SWING Une application graphique à un containeur principal qui contient tous les autres. 3 types différents dans SWING : Jframe, Jdialog et Japplet Ils contiennent tous des widgets qui sont contenus dans le content pane JFrame Public static void main (String[] args) { JFrame frame = new JFrame (Ma première fenêtre); frame.setVisible(true); } Dialog Public static void main (String[] args) { JFrame frame = new JFrame (« Ma première fenêtre »); frame.setVisible(true); JDialog dialog = new JDialog (frame, « Hello world », true); } Modale Exercice Identifier les widgets de cette interface Facettes des widgets les « widgets » bouton menu fenêtre barre d’outils onglet étiquette zone de texte bouton radio liste barre de défilement « slider » Facettes des widgets Présentation apparence graphique Comportement réactions aux actions de l’utilisateur Interfaçage avec l’application : notifications de changement d’état Facettes des widgets Exemple : Bouton Présentation cadre avec un nom à l’intérieur Comportement « enfoncement » ou « relâchement » lorsque l’on clique dessus Interfaçage avec l’application : fonction appelée lorsque le bouton est cliqué Facettes des widgets Exemple : Slider Présentation ? Comportement ? Interfaçage avec l’application : ? Facettes d’un widget Notifications de changement d’état : – variables actives (Tcl/Tk) – envoi d’évènements(Qt) – fonctions de rappel (« callbacks ») Variables actives lien bidirectionnel entre une variable d’état du widget et une variable de l’application problèmes – limité aux types simples – lien de retour peut être coûteux – Erreurs si les liens sont mis à jour par des développeurs Envoi d’évènements Les widgets agissent comme des périphériques d’entrée et envoient des évènements. Une boucle tant que lit et traite les évènements Association d’un objet à un widget et de méthodes de l’objet aux changements d’état Envoi d’évènements Sépare l’envoi et le traitement des évènements Meilleure encapsulation Fonctions de rappel Enregistrement lors de la création du widget Appel lors l’activation du widget Fonctions de rappel Problème : spaghetti des callbacks Partage d’état entre plusieurs callbacks par : – variables globales • Trop dans une application réelle – arbre des widgets : la fonction de rappel est appelée en lui passant le widget qui l’a déclenché • Fragile si l’on change la structure, insuffisante pour d’autres données pas associés aux widgets – « jeton » (token) : donnée enregistrée avec la callback, passée automatiquement au moment de l’appel fonctions de rappel /* fonction de rappel */ void DoSave (Widget w, void* data) { /* récupérer le nom de fichier */ filename = (char**) data; /* appeler la fonction de l’application */ SaveTo (filename); /* fermer la boîte de dialogue */ CloseWinfow (getParent(getParent(w))); } /* programme principal */ main () { /* variable contenant le nom du fichier */ char* filename = “”; … /* créer le widgets et lui associer sa callback */ ok = CreateButton (....); RegisterCallback (ok, DoSave, (void*) &filename); … /* boucle de traitement des événements */ MainLoop (); } « event listeners » (Java) Variante des callbacks adaptée au Java: Méthodes de type AddListener spécifient non pas une fonction de callback, mais un objet (le listener) Lorsque le widget change d’état, il déclenche une méthode prédéfinie du listener (par exemple actionPerformed) « event listeners » (Java) public class ClickListener implements ActionListener { public void actionPerformed(ActionEvent e){ JButton button = (JButton)e.getSource(); … } } … ClickListener listener = new ClickListener(); JButton button = new JButton(’’Click me’’); button.addActionListener(listener); … « event listeners » (Java) Anonymous Inner classes … button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ … } }); … panel.addMouseListener(new MouseAdapter(){ public void mouseClicked(MouseEvent e){ … } }); Fonctions et évènements prédéfinis « event listeners » (Java) Anonymous Inner classes “new <nom-de-classe> () { <corps> }” cette construction fait deux choses : § elle crée une nouvelle classe, sans nom, qui est une sous-classe de <nom-de-classe> définie par <corps> § elle crée une instance (unique) de cette nouvelle classe et retourne sa valeur Cette class a accès aux variables et méthodes de la classe dans la quelle elle est définie Evènements et Ecouteurs Chaque évènement à une source (Jbutton, JRadioButton, Jmenu, JTextField) à laquelle on accède via la méthode getSource() Les listeners doivent implémenter l’interface qui correspond aux évènements. Ex : ActionEvent => ActionListener Evènements et Ecouteurs Tous les évènements héritent de la classe EventObject Tous les listeners héritent de EventListener Quelques couple Events – Listener • ActionEvent ActionListener • MouseEvent MouseListener • KeyEvent KeyListener • … Evènements et Ecouteurs Les listeners doivent être enregistrés (ajoutés) aux widgets. Un listener peut être ajouté à plusieurs widgets. Un listener pour plusieurs boutons par exemple Un widget peut avoir plusieurs listeners Un pour le click et un pour la touche entrée sur un bouton Exercice Identifier les listeners de cette interface « drag-and-drop » Quels sont les « widgets » affectés ? Quels sont les évènements? Exercice : comment décrire cette interaction avec un « event listener » ? « drag-and-drop » Quels sont les « widgets » affectés ? Quels sont les évènements? Exercice : comment décrire cette interaction avec un « event listener » ? http://openclassrooms.com/courses/apprenez-a-programmer-en-java/le-drag-n-drop