TP 06 : wxWidgets (Partie 03)
Transcription
TP 06 : wxWidgets (Partie 03)
Université de Strasbourg UFR de Mathématiques et d’Informatique Département d’Informatique Licence 3 d’Informatique IHM Année 2010 – 2011 TP 06 : wxWidgets (Partie 03) Objectif général Le but des 5 prochains TP va être d’apprendre à créer et manipuler des interfaces graphiques, en créant un petit programme destiné à l’affichage et à la saisie de cercles. Pour cela, nous allons utiliser la librairie multi-plateforme wxWidgets (anciennement wxWindows), à titre d’exemple. Vous devrez programmer en C++. Tout au long de ces TP, vous pourrez utiliser la documentation présente sur le site web de wxWidgets : – http://docs.wxwidgets.org/stable/ – http://wiki.wxwidgets.org/Main_Page Objectif de ce TP Dans ce 3ème TP, nous allons introduire la structure de données cercle, et voir comment interagir entre l’application et des fichiers. 1 1.1 Entrées/Sorties fichiers Structure de données cercle Téléchargez l’archive tpwx3 files.tar.bz2. Dans cette archive vous y trouverez les fichiers circle.h et circle.cpp qui contiennent la définition et l’implantation de la classe Circle, qui contient un sommet center, un entier radius représentant le rayon du cercle, une couleur colour, et une épaisseur de trait thickness. Différents constructeurs et méthodes utilitaires sont également présents pour vous aider. 1.2 1.2.1 Chargement d’un fichier cercle Format de fichier Le fichier cercles.cir contient un exemple de fichier texte contenant des cercles. Il est écrit dans le format que nous avons choisi pour sauvegarder des fichiers. Ce format est constitué d’une première ligne contenant un entier n représentant le nombre de cercles contenus dans le fichier. Puis, après une ligne vide, viennent n blocs de données contenant chacun 3 lignes : – la première contient 3 entiers correspondant aux coordonnées 2D du centre du cercle et de son rayon : xyr – la seconde contient 4 entiers compris entre 0 et 255 qui correspondent à la couleur de l’intérieur du cercle au format RGBA. – la troisième contient un entier représentant l’épaisseur de trait du bord du cercle. Ajoutez à la classe MainFrame une variable protégée entière num circle, qui représentera le nombre de cercles à afficher, et qui devra être initialisée à 0 au lancement de l’application ou lorsqu’on recommence un dessin. Ajoutez également une variable protégée tab circle de type tableau de cercles qui va (pour simplifier) contenir un maximum de 5 cercles (ou pointeurs sur des cercles). 1 Pour les plus courageux Au lieu d’utiliser un tableau de taille fixe, vous pouvez utiliser une liste de pointeurs sur des cercles définit par : CircleList circleList ; Le type CircleList est défini par la macro WX DECLARE LIST(Circle, CircleList); du fichier circle.h. Cette classe CircleList dérive du type wxList. Son implantation est implicite et est faite par l’appel aux commandes dans circle.cpp : #include <wx/ l i s t i m p l . cpp> WX DEFINE LIST( C i r c l e L i s t ) ; Dans ce cas, la variable num circle n’est plus nécessaire car le nombre de cercles peut-être obtenu via la fonction GetCount qui retourne le nombre d’éléments dans la liste. Cette liste définit une liste de pointeurs vers des objets de type Circle. Il faut donc les allouer dynamiquement (via l’opérateur new) avant de les insérer dans la liste (fonction Append, pour l’ajout en queue de liste). Par défaut, la suppression d’un élément de la liste de cercles via les fonctions Clear ou remove n’entraine pas la destruction du cercle (il reste alloué en mémoire). Pour modifier ce comportement et réellement détruire les objets de la liste, il faut appeler la commande : c i r c l e L i s t . D e l e t e C o n t e n t s (TRUE) ; dans le constructeur de MainFrame (si la liste s’appelle circleList). 1.2.2 Lecture/Écriture Lorsqu’un utilisateur choisit de cliquer sur Ouvrir ou sélectionne l’icône d’ouverture de fichier, l’application devra ouvrir un fichier cercle, dont le format sera celui décrit ci-dessus, en demandant à l’utilisateur de choisir le fichier à ouvrir. Pour cela, on dispose de la classe wxFileDialog de wxWidgets, qui permet d’ouvrir une boı̂te de dialogue de choix de fichier, en spécifiant quelques options du type : – répertoire par défaut, – nom par défaut, – extension par défaut, – etc. Parmi ces options, se trouve également le style de boı̂te de dialogue à afficher en fonction du but de ce choix de fichier : ouverture ou sauvegarde. Dans le cas présent, nous choisirons le style wxOPEN (ce qui indique une boı̂te d’ouverture de fichier). Regardez dans la doc pour voir le fonctionnement de cette classe. Lorsqu’on affiche une telle boı̂te de dialogue, si l’utilisateur clique sur OK, alors on doit ouvrir le fichier choisi. Pour cela, on va utiliser les classes de gestion de flux d’entrées/sorties de wxWidgets (penser à inclure les fichiers wx/wfstream.h et wx/txtstrm.h) : // Ouvre l e f i c h i e r wxFileInputStream f i l e ( f i l e d i a l o g . GetPath ( ) ) ; // Test s i l ’ o u v e r t u r e a r e u s s i i f ( f i l e . IsOk ( ) ) { // Transforme l e f i c h i e r en un f l u x de t e x t e wxTextInputStream i n ( f i l e ) ; // I c i l e s commandes pour l i r e } else { //Une e r r e u r d ’ o u v e r t u r e du f i c h i e r // s ’ e s t p r o d u i t e . } qui ouvre un fichier en lecture pour récupérer son contenu (c’est la version wxWidgets de fopen et fclose). Dans l’exemple ci-dessus, la boı̂te de dialogue de choix de fichier s’appelait filedialog, on a récupéré le nom complet (sous forme d’un wxString) du fichier choisi grâce à la fonction GetPath. Pour ouvrir un fichier en écriture, il faut utiliser un wxFileOutputStream et un wxTextOutputStream au lieu des wxFileInputStream et wxTextInputStream. 2 Si l’ouverture de ce fichier échoue, il faut afficher un message d’erreur : // i f open f i l e f a i l e d , show an e r r o r message box wxString errormsg , c a p t i o n ; e r r o r m s g . P r i n t f (wxT( ” Unable t o open f i l e ” ) ) ; e r r o r m s g . Append ( f i l e d i a l o g . GetPath ( ) ) ; c a p t i o n . P r i n t f (wxT( ” E r r e u r ” ) ) ; wxMessageDialog msg ( this , errormsg , c a p t i o n , wxOK | wxCENTRE | wxICON ERROR ); msg . ShowModal ( ) ; return ; Notez dans cet exemple que le test d’échec d’ouverture se fait simplement en testant utilisant la fonction IsOk définie dans la classe wxFileInputStream ou dans la classe wxFileOutputStream. On peut alors commencer à récupérer les données selon le modèle suivant : i n >> n u m c i r c l e ; i n >> x >> y >> r ; Les étranges >> sont appelés opérateurs d’extraction. En gros, ils indiquent simplement que l’on veut lire une donnée du flux in, et qu’on veut la ranger dans num circle. Ce qui est particulièrement sympathique ici c’est qu’il n’est pas nécessaire de spécifier le type de la valeur à lire (entier, chaı̂ne, flottant, etc.) : le compilateur le détermine automatiquement en fonction du type de la variable dans laquelle on veut stocker la valeur (polymorphisme). Bien entendu, il faut que ce qui est lu du fichier corresponde (par exemple, lire abcde dans un entier ne fera pas quelque chose de très intéressant...). Notons également au passage que les espaces et retours à la ligne sont interprétés comme des séparateurs pour les différentes valeurs à lire. Une fois qu’on a récupéré toutes les données du fichier et qu’on les a placées dans tab circle (ou circleList), il faut penser à activer le bouton de Gestion des cercles dans le menu Options. Ce bouton ne doit être actif que si tab circle (ou circleList) contient des cercles à afficher. 1.2.3 Sauvegarde d’un fichier cercle Nous allons maintenant nous intéresser à l’opération inverse qui est celle de la sauvegarde de fichiers cercles. Cette opération ressemble beaucoup à la précédente, excepté que le style de la boı̂te de dialogue sera wxSAVE, agrémenté d’un wxOVERWRITE PROMPT, que l’ouverture du fichier pour écriture dans ce fichier (et non plus simple lecture) se fait par : // Ouvre l e f i c h i e r en e c r i t u r e wxFileOutputStream f i l e ( f i l e d i a l o g . GetPath ( ) ) ; // Test s i l ’ o u v e r t u r e a r e u s s i i f ( f i l e . IsOk ( ) ) { // Transforme l e f i c h i e r en un f l u x de t e x t e wxTextOutputStream out ( f i l e ) ; // I c i l e s commandes pour e c r i r e } else { //Une e r r e u r d ’ o u v e r t u r e du f i c h i e r // s ’ e s t p r o d u i t e . } et que l’écriture dans un fichier se fait par des instructions de type : out << num circle ; sans oublier d’ajouter des caractères de fin de ligne : 3 out << wxT( ” \n” ) ; Vous noterez la similitude entre les >> de tout à l’heure et les <<. En effet, ces opérateurs, appelés opérateurs d’insertion, font exactement l’inverse des opérateurs d’extraction : ils écrivent dans le fichier, une valeur formatée correctement en fonction du type de la variable écrite. 2 Liens avec les événements Il faut maintenant s’arranger pour que les événements qui peuvent survenir sur les widgets aient un effet sur le tableau de cercles et réciproquement : – la boı̂te de dialogue de gestion des cercles doit refléter le contenu de ce tableau, et lorsqu’on choisit de visualiser les propriétés d’un des cercles, celui-ci doit être mis à jour. – on doit pouvoir supprimer un cercle de la liste grâce au bouton supprimer. – la suppression de tous les cercles doit entraı̂ner la désactivation du bouton de Gestion des cercles. – L’appui sur le bouton Nouveau doit effacer tout. À vous de jouer pour toutes ces fonctionnalités... Et à la prochaine séance, on affichera ces cercles avec OpenGL. 4