Tutoriel wxWindows
de David BEECH traduction française par Patrick VALERI
Section C++ du site http://phenix.developez.com créé le 20 Novembre 2003
Remis à jour 17 Septembre 2004
Programmez avec wxWindows
Septième Étape
Créer d'autres cadres (frames)
Introduction
Toutes les sessions précédentes n'utilisaient qu'un seul cadre. Nous ne sommes pas limités à un seul cadre, nous pouvons en utiliser tant qu'on veut et notre 1 ère étape sera de créer une application simple qui contienne plusieurs instances du cadre original. Ceci est plus proche d'une application concrète.
Sur la saisie d'écran à gauche nous pouvons voir le résultat. A l'arrière-plan se trouve le cadre original et dessiner au-dessous d'autres cadres. Chaque cadre ajouté a les mêmes propriétés que l'original.
#include "basic.h"
#include "basicframe.h"
IMPLEMENT_APP(BasicApplication)
bool BasicApplication::OnInit()
{
appTitle = TITLE;
appTitle.Append(ABOUT);
BasicFrame * frame =
new BasicFrame(appTitle, 50, 50, 450, 300);
frame->Show(TRUE);
SetTopWindow(frame);
return TRUE;
} |
Initialement j'ai adopté une approche très simple. J'ai pris notre BasicFrame, avec toutes ses propriétés, et en est fait une classe avec tous ces droits. Vous pouvez voir que nous avons un fichier d'en-têtes basicframe.h et nous créons une instance de BasicFrame nommée frame .
Vous pouvez voir BasicFrame ici.
Nous ajoutons une nouvelle méthode à notre BasicFrame: OnNewFrame :
void BasicFrame::OnNewFrame (wxCommandEvent & event)
{
wxString theTitle;
theTitle =
wxGetTextFromUser("Enter a title", "Input text", "NO NAME", this, -1,-1,TRUE);
BasicFrame * win = new BasicFrame(theTitle, 150, 150, 450, 300);
windowList->Append(win);
win->Show(TRUE);
}
Nous modifions le destructeur pour être correct et nous indiquer chaque cadre détruit:
BasicFrame::~BasicFrame()
{
if (windowList->GetCount() > 1)
{ wxMessageDialog
destructDialog ( this, "Destroying window", "In BasicFrame destructor", wxOK);
destructDialog.ShowModal();
}
}
Nous ajoutons une méthode juste pour indiquer combien de cadres ont été créés:
void BasicFrame::OnCount (wxCommandEvent & event)
{ char buf[20];
sprintf(buf, "%d frames", windowList->GetCount());
wxMessageDialog
countDialog ( this, buf, "Frames", wxOK);
countDialog.ShowModal();
}
En dehors de ces différences, il y a un changement par rapport à nos exemples précédents.
Si vous souhaitez voir un exemple alors téléchargez wxbasic7.zip et lisez le code source.
La suite de la création de cadre par ce moyen est que chacun des cadres est entièrement indépendant des autres cadres, il n'y a pas de rapport parent->enfant. Cela peut être souhaité mais vous pouvez aussi vouloir un comportement qui reflète un lien parent->enfant. Quand un cadre parent est détruit, vous voulez que tous les cadres enfants soient détruits aussi.
Implémentation des hiérarchies de cadre
Si vous lisez la documentation wxWindows vous y trouverez un modèle commun que chaque fenêtre peuvent avoir un pointeur vers la fenêtre parent.
wxWindow( wxWindow* parent,
wxWindowID id,
...
)
Si nous construisons une fenêtre et remettons un pointeur « parent » au constructeur alors nous établissons un lien parent->enfant entre les fenêtres créatrices (parent) et créés (child).
Le schéma montre cela. La plus haute fenêtre est la fenêtre parent de la fenêtre 1 qui est elle-même la fenêtre parent des fenêtres 1a et 1b. La fenêtre 1b est la fenêtre parent de 1b.1 et la plus haute fenêtre est l'arrière grand-parent de 1b.1. Réaliser cela est tout à fait simple.
Dans l'en-tête (basicframe.h) nous modifions le constructeur de basicframe de façon qu'il contienne un a pointeur vers le parent:
BasicFrame( wxWindow * parent,
const wxChar *title,
int xpos,
int ypos,
int width,
int height);
Nous reportons cela dans la classe du constructeur et livrons le parent au constructeur de wxFrame:
BasicFrame::BasicFrame
( wxWindow * parent,
const wxChar *title, int xpos, int ypos, int width, int height)
: wxFrame( parent, -1, title,
wxPoint(xpos, ypos),
wxSize(width, height))
BasicFrame::~BasicFrame()
{
}
Dans l'application, nous démarrons avec un pointeur NULL puisqu'il n'y a pas de parent à cette fenêtre:
BasicFrame * frame = new BasicFrame( wxWindow * NULL,
appTitle, 50, 50, 450, 300);
Quand nous fermons une fenêtre parent toutes les fenêtres filles sont automatiquement détruites ! Si nous utilisons la méthode de fermeture des fenêtres, cela ferme et détruit aussi toutes les fenêtres.
Vous pouvez aussi placer une série de lien de fermeture des fenêtres mais de loin le moyen le plus sûr est de laisser la méthode Close le faire pour vous.
Close appellera le destructeur et tout ira pour le mieux. Vous pouvez aussi surcharger Close et introduire d'autres comportements comme nous avons vu dans plusieurs exemples mais il y a un petit problème avec cet exemple en particulier quand il vient de fermer le cadre parent. Si le contenu du contrôle de texte d'un cadre fille a changé et que nous sortons du cadre parent alors nous perdons les changements dans le contrôle de texte. Nous reviendrons là-dessus plus tard mais en premier j'aimerais introduire un sujet : la communication avec la fenêtre parent.
Liens d'un cadre enfant à un cadre parent
La communication d'un cadre parent vers un cadre enfant n'est pas difficile. Le parent « connaît » l'enfant puisqu'il le créait et l'accès à l'enfant et ses membres publics est simplement une question de référence à l'enfant comme thisChild->publicMember ou thisChild.publicMember à la condition que la référence soit vers un objet crée dynamiquement ou crée statiquement. Liens par d'autres possibilités: enfant à parent, n'est pas si direct puisque la seule référence que l'enfant puisse avoir avec le parent est un pointeur passé au cadre enfant quand il est créé. Voici trois possibilités qui nous permettent de conclure sur ce type de liens:
Utilisation d'une variable globale
Utilisation d'une référence parent passée au constructeur enfant, et
"Découverte" du parent en utilisant une méthode de fenêtre.
Dans cet exemple, j'ai séparé le code original basicframe en deux classes : mainframe et basicframe . Il ne peut y avoir qu'un seul mainframe , c'est le cadre parent mais il peut y avoir beaucoup de basicframe s qui sont les cadres enfants.
Le fichier d'implémentation pour cette application contient une variable globale parentFrame qui est de type Mainframe .
//basic.cpp
#include "basic.h"
#include "basicframe.h"
#include "mainframe.h"
MainFrame * parentFrame;
IMPLEMENT_APP(BasicApplication)
bool BasicApplication::OnInit()
{
appTitle = "Step 7b";
parentFrame = new MainFrame((wxWindow *) NULL, appTitle, 50, 50, 450, 300);
parentFrame->Show(TRUE);
SetTopWindow(parentFrame);
return TRUE;
}
Le fichier d'implémentation pour la classe basicframe contient une déclaration externe indiquant que parentFrame est définie autre part :
#include "basic.h"
#include "basicframe.h"
#include "mainframe.h"
extern MainFrame * parentFrame;
BasicFrame::BasicFrame
( wxWindow * parent,
const wxChar *title, int xpos, int ypos, int width, int height)
...
La classe mainframe contient une méthode nommée ChildMessage comme équivalent au contrôle de texte theText que nous avons utilisé durant nos séries d'exemples. Nous utilisons ces deux membres Mainframe comme les cibles pour les liens enfants.
void MainFrame::ChildMessage(wxString s)
{ theText->AppendText(s); }
Utilisation d'une variable globale
Utilisation d'une instance globale parentFrame est directe:
parentFrame->ChildMessage("\n A Message from the child ...\n");
Vous devez avoir entendu parler par un professeur zélé ou un conférencier que l'utilisation de variables globales est un péché. Ça l'est, mais seulement un péché vénal, cela ne vous exposera pas à l'enfer des programmeurs. L'utilisation de trop nombreuses variables globales, ça, c'est réellement mauvais et peut vous plonger dans de nombreux problèmes. La question est: Trop, c'est combien? La réponse est: cela dépend du nombre d'effets de bord indésirables qui risque de se produire. Préoccupez-vous de ça.
Utilisation de référence parent passé au constructeur enfant :
Quand nous créons un cadre enfant, comme ceci:
BasicFrame * win = new BasicFrame(this, theTitle, 150, 150, 450, 300);
Nous livrons un pointeur à this , l'instance de classe responsable de la création du cadre enfant. Quand nous voulons nous référer au cadre parent, nous devons fournir la référence que nous recevons au pointeur de type MainFrame :
((MainFrame*)p)->theText->AppendText("\nWelcome\n\n");
Cela semble mal venu mais oriente naturellement le compilateur à interpréter le pointeur de type wxWindow comme un pointeur MainFrame. Nous pouvons légitimement faire cela parce que le pointeur est un pointeur vers un objet MainFrame.
"Découverte" du parent en utilisant une méthode de fenêtre :
Une fenêtre peut déterminer si elle a un parent en utilisant la fonction membre de fenêtre GetParent():
((MainFrame*)GetParent())->ChildMessage(x);
A nouveau, nous devons fournir la référence.
Si vous souhaitez en voir un exemple alors téléchargez wxbasic7b.zip et lisez le code source.
Sommaire
Un petit rappel : En présentant cela je n'ai montré que les détails importants. Vous devez vous rappeler que les exemples contiennent aussi d'autres codes pertinents, par exemple l'identifiant de fenêtres et la table d'événements. J'ai allége les programmes d'exemples pour la présentation dans le but de garder de l'espace et de ne pas s'égarer.
Retour en début de document
Traduction de Patrick VALERI du document de David Beech
Copyright © 1999 - 2001 David Beech
Ce document est issu de http://phenix.developpez.com/ et reste la propriété exclusive de son auteur.
La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.
Copyright © 2000-2005