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