Une application peut utiliser plusieurs fenêtres, s'appelant les unes les autres. En général, on aura une fenêtre principale qui permet d'appeler des fenêtres secondaires. Nous allons voir ici comment réaliser une application de ce type en partant directement d'un exemple. Il s'agit d'une nouvelle version du visualisateur d'images, qui permet cette fois-ci de visualiser simultanément deux images dans deux fenêtre distinctes. Vous trouverez ce projet dans le répertoire Exemple-ProgObjet1/MultiFenetrage.
Il y a donc en tout trois fenêtres: une fenêtre principale ainsi que deux fenêtres secondaires pour visualiser les images. La fenêtre principale permet de commander l'affichage.
La fenêtre principale comporte un menu, une liste de fichiers et deux boutons radios :
Les deux entrées du menu Voir permerttent de faire apparaitre les fenêtres (elles sont invisibles au démarrage de l'application). La liste de fichier permet de sélectioner une image à affoicher. Les boutons radio quand à eux définissent dans quelle fenêtre sera afficher l'image sélectionnée.
Voyons tout d'abord comment créer une application multi-fenêtres sous Lazarus. Lorsque vous démarrez la réalisation d'une nouvelle application, vous avez une seule fenêtre:
Par défaut, la classe représentant cette fenêtre est TForm1, sous classe de TForm et l'objet représentant votre fenêtre est Form1, déclaré comme objet de la classe TForm1.
L'unité associée à cette classe se nomme par défaut Unit1 et son code sera contenu dans le fichier Unit1.pas.
Pour ajouter une nouvelle fenêtre, sélectionnez l'entrée Nouvelle Fiche du menu Fichier. Cela fera apparaitre une nouvelle fenêtre nommée Form2:
La classe de cette fenêtre se nomme à présent TForm2 et l'unité associée Unit2 sera par défaut enregistrée dans le fichier Unit2.pas.
Dans une application multi-fenêtres, la fenêtre principale est celle qui va s'afficher en premier au démarrage de l'application (par défaut les fenêtres secondaires ne s'affichent pas). C'est donc par l'intermédiaire de cette fenêtre que les fenêtres secondaires pourront être affichées. Lorsque l'utilisateur ferme la fenêtre principale, les autres fenêtres sont automatiquement fermées. Par contre l'inverse n'est pas vrai: si l'utilisateur ferme une fenêtre secondaire, la fenêtre principale reste ouverte.
La fenêtre principale joue donc un rôle particulier par rapport aux autres fenêtres.
Mais comment Lazarus sait-il quelle fenêtre sera la fenêtre principale ?
Par défaut, c'est la première fenêtre créée.
Si par mégarde vous avez commencé par une fenêtre secondaire, vous pouvez redéfinir la fenêtre principale en éditant le fichier .lpr.
Voici, par exemple le contenu du fichier lpr pour un projet à deux fenêtres:
program project1; {$mode objfpc}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset Forms, Unit1, Unit2 { you can add units after this }; {$R *.res} begin RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end.
Si vous voulez que Form2 soit la fenêtre principale au lieu de Form1, il suffit d'inverser les appels de la méthode CreateForm:
program project1; {$mode objfpc}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset Forms, Unit1, Unit2 { you can add units after this }; {$R *.res} begin RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm2, Form2); Application.CreateForm(TForm1, Form1); Application.Run; end.
De manière générale, la fenêtre principale est celle qui correspond au premier appel de la méthode CreateForm.
Dans une application multi-fenêtres, je vous conseille de renommer les unités et les fenêtres. Car des noms comme Form1, Form2, Unit1, Unit2 ... etc, sont très peu évocateurs.
Le nommage des unités se fait au moment de la sauvegarde du projet, car le nom d'une unité est lié au nom du fichier pascal qui la contient.
Par exemple, pour changer le nom de l'unité Unit1 en UnitePrincipale sélectionnez l'entrée Enregistrer le projet sous .. dans le menu Projet. Vous aurez à un moment donné une boite de dialogue vous demandant d'enregistrer Unit1.pas:
Il suffit alors de remplacer Unit1.pas par UnitePrincipale.pas:
et l'unité Unit1 sera alors automatiquement renommée UnitePrincipale dans l'éditeur de source:
Pour renommer les fenêtres, il suffit de modifier la propriété name de Form1, Form2, etc via l'inspecteur d'objets.
Dans notre exemple, nous avons utilisé Frm_Principal pour la fenêtre principale, Frm1 pour la fenêtre 1 et Frm2 pour la fenêtre 2.
Remarquez que le renommage entraine automatiquement le renommage des classes associées. Dans notre exemple, Form1 a été renommé Frm_Principal ce qui a automatiquement entrainé le renommage de TForm1 en TFrm_Principal:
type TFrm_Principal = class(TForm) . . end; var Frm_Principal : TFrm_Principal;
Notre exemple de projet multi-fenêtres contient trois unités nommées UnitePrincipale (associée à la fenêtre principale) , UniteImage1 (associée à la fenêtre 1) et UniteImage2 (associée à la fenêtre 2).
En ouvrant le projet, vous constaterez que UniteImage1 et UniteImage2 sont importées dans UnitePrincipale:
unit UnitePrincipale; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Menus, FileCtrl, StdCtrls, UniteImage1, UniteImage2;
Cette importation est nécessaire car l'unité principale contient des instructions agissant sur les composants des deux autres fenêtres. De manière générale
Pour bien comprendre le code de notre exemple , il est préférable d'avoir lu les parties du cours concernant les , menus, les liste de fichiers, les boutons radios et les composants image.
Précisons que toutes les procédures évènementielles décrites ici se trouvent dans l'unité UnitePrincipale associée à la fenêtre principale.
Voici la procédure évènementielle gèrant le clic sur une entrée de la liste des fichiers:
procedure TFrm_Principal.FileListBox1Click(Sender: TObject); var nom_fichier : string; begin nom_fichier:= FileListBox1.Directory+ '\' +FileListBox1.GetSelectedText; if BR_Fenetre1.Checked then Frm1.Image1.Picture.LoadFromFile(nom_fichier) else Frm2.Image1.Picture.LoadFromFile(nom_fichier); end;
FileListBox1 est le nom de la liste des fichiers.
La première instruction construit le nom complet du fichier image à partir du chemin du répertoire qui la contient (stocké dans l'attribut Directory du composant FileListBox) et du nom de fichier sélectionné par l'utilisateur.
Le bouton radio étiqueté Fenêtre 1 se nomme BR_Fenetre1. S'il est coché, l'image est affichée dans la fenêtre 1. Sinon, elle est affichée dans la fenêtre 2.
Comme les composants image ne se trouvent pas dans la fenêtre principale, il est nécessaire de préfixer leurs noms par le nom de la fenêtre qui les contient. On accède donc ici au composant image de la fenêtre 1 par Frm1.Image1 et au composant image de la fenêtre 2 par Frm2.Image2.
L'entrée Fenetre1 du menu Voir déclenche l'exécution de la procédure suivante:
procedure TFrm_Principal.MN_Voir_Fenetre2Click(Sender: TObject); begin Frm2.Show; end;
On applique la méthode Show à Frm2 pour faire apparaitre la fenêtre.
L'entrée Fenetre2 du menu Voir utilise le même principe pour la deuxième fenêtre:
procedure TFrm_Principal.MN_Voir_Fenetre1Click(Sender: TObject); begin Frm1.Show; end;