Création d'un modèle de projet

Avec Lazarus

Alcatîz

La création d'un modèle de projet personnalisé peut vous faire gagner du temps en début de développement, si vous êtes amené(e) à utiliser les mêmes éléments dans plusieurs projets. Découvrez les étapes de la création d'un modèle de projet Lazarus à l'aide d'un exemple concret.

Commentez

- Titre : Création d'un modèle de projet

- Auteur : Alcatîz

- Parution : 9 septembre 2019

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2019 Alcatîz. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Création du projet qui servira de modèle

- II-A. Partie interface

- II-B. Partie métier

- III. Enregistrement du projet

- IV. Transformation du projet en modèle

- IV-A. Création du fichier project.ini

- IV-B. Implémentation des variables du modèle

- IV-B-1. Renommage des fichiers

- IV-B-2. Implémentation des variables dans les fichiers

- V. Utilisation du modèle

- VI. Remerciements

I - Introduction

Lorsque vous créez un nouveau projet avec Lazarus, que ce soit par le menu « Projet / Nouveau projet… » ou par le menu « Fichier / Nouveau… », vous pouvez faire votre choix parmi plusieurs types de projets : application graphique standard, application console, bibliothèque, etc. Il s'agit de modèles standard de projet, dans lesquels sont prédéfinies une série d'options de compilation, d'unités déclarées par défaut et une interface minimale vierge.

Il est possible d'aller plus loin et de créer vous-même un modèle de projet personnalisé contenant plus d’éléments prédéfinis, afin de gagner du temps en début de développement. Cette possibilité n'a évidemment de sens que si vous êtes amené(e) à utiliser les mêmes éléments dans plusieurs projets.

La création d'un modèle de projet n'est pas très compliquée, il est même possible de partir d'un projet existant. Dans ce tutoriel, nous allons prendre comme exemple un modèle d'application de test possédant une fenêtre de débogage masquable, dans laquelle des informations peuvent être affichées pendant l'exécution.

II - Création du projet qui servira de modèle

II-A - Partie interface

Créez un nouveau projet de type « Application ».

Les propriétés de la fiche principale resteront telles quelles.

L'élément prédéfini dans le modèle sera la fenêtre de débogage : créez-la via le menu « Fichier / Nouvelle fiche » ou via le second bouton de la barre d'outils.

Dimensionnez cette seconde fiche selon vos souhaits, nommez-la par exemple frmDebug (propriété Name) et donnez-lui le titre que vous voulez (propriété Caption).

Déposez-y un composant de type TMemo, que vous nommez par exemple mmoDebug (propriété Name). Réglez son alignement sur alClient (propriété Align), forcez-le en lecture seule (propriété ReadOnly) et effacez son contenu (propriété Lines, clic sur les trois points puis « Effacer »).

Retournez à la fiche principale et déposez-y un composant de type TMainMenu, c'est-à-dire une barre de menu. Nous considérons donc que les applications qui seront créées d'après ce modèle possèderont un menu principal ; tout ce que nous allons faire sera de créer un sous-menu permettant d'afficher ou de masquer la fenêtre de débogage. Double-cliquez sur le composant afin d'ouvrir l'assistant de création de menu.

Créez un menu, appelez-le par exemple mnuWindow (propriété Name) et donnez-lui comme libellé « &Window » ou « &Fenêtre » (propriété Caption). Affublez-le d'un sous-menu mnuWindowDebug (propriété Name), avec le libellé de votre choix (propriété Caption), et cochez sa propriété AutoCheck afin d'afficher une case à cocher à côté du libellé :

II-B - Partie métier

Occupons-nous de l'affichage ou du masquage de la fenêtre de débogage.

Tout d'abord, ajoutez l'unité ufrmDebug à la clause uses de l'unité principale.

Ensuite, créez le gestionnaire d'événement OnClick du menu mnuWindowDebug. Son contenu est tout simple :

procedure TForm1.mnuWindowDebugClick(Sender : TObject);
(* Shows or hides the debug window *)
begin
frmDebug.Visible := mnuWindowDebug.Checked;
end;

Ainsi, en fonction de l'état de la propriété Checked du menu, la fenêtre de débogage sera affichée ou masquée (étant entendu qu'au démarrage de l'application, la fenêtre sera invisible).

Prenons soin d'empêcher que la fenêtre frmDebug soit directement fermée, en créant un gestionnaire pour l'événement OnCloseQuery :

procedure TfrmDebug.FormCloseQuery(Sender : TObject; var CanClose : boolean);
(* Prevents the window from being closed *)
begin
CanClose := false;
end;

De cette manière, le menu système de la fenêtre permettra que celle-ci soit redimensionnée, minimisée ou maximisée, mais pas fermée. Pour la masquer, il faudra passer par le menu de la fenêtre principale de l'application.

III - Enregistrement du projet

Enregistrez votre projet, mais pas n'importe où :

L'endroit où Lazarus s'attend à trouver les modèles de projets est le sous-dossier .lazarus/templates de votre dossier personnel.

C'est donc aussi à cet endroit que vous devez copier un projet existant si vous désirez en faire un modèle de projet.

Attention que sous Linux et macOS, les dossiers dont le nom commence par un point sont invisibles par défaut ; il vous faudra peut-être activer l'affichage des fichiers cachés dans le dialogue d'enregistrement.

Enregistrez le projet sous le nom debugtemplate (par exemple) dans un nouveau sous-dossier du même nom sous le dossier .lazarus/templates. Laissez le nom Unit1 par défaut pour la fiche principale et donnez un nom à l'unité de la seconde fiche (ufrmDebug, pour rester cohérent avec le nom donné à la fiche).

IV - Transformation du projet en modèle

Faisons le point :

- nous avons créé un projet debugtemplate contenant une fiche principale Form1 (nom laissé par défaut) et une seconde fiche frmDebug ;

- la fiche Form1 contient un menu principal MainMenu1 (nom laissé par défaut), qui lui-même contient un menu mnuWindow avec un élément mnuWindowDebug dont nous avons défini le gestionnaire d'événement OnClick ;

- la fiche frmDebug contient un composant TMemo appelé mmoDebug et le gestionnaire d'événement OnCloseQuery.

Pourquoi avons-nous laissé inchangés les noms de la fiche principale et du menu principal ? Parce que ces noms seront définis au moment de la création du projet basé sur notre modèle. Nous allons même en faire des variables du modèle.

À ce stade, que nous ayons créé un projet exprès (comme nous venons de le faire) ou bien recopié un projet existant dans le sous-dossier .lazarus/templates, les actions qui vont suivre sont identiques.

IV-A - Création du fichier project.ini

Dans le dossier du modèle, créez un fichier texte appelé project.ini (ce nom est obligatoire). Ce fichier contient les informations nécessaires à Lazarus pour créer un projet basé sur le modèle. Voici son contenu :

[Variables]
MAINUNIT=New filename for the Unit1.pas file
MAINFORM=Main form name
MAINMENU=Main menu name
[Project]
Name=Custom debug application
Author=Alcatiz
Description=GUI application with a debug window

Dans la première section [Variables], nous définissons trois variables pour le modèle :

- MAINUNIT, le nom de l'unité principale ;

- MAINFORM, le nom de la fiche principale ;

- MAINMENU, le nom du menu principal.

Ce qui est inscrit après le signe « = » est le commentaire associé à la variable.

Dans la section [Project] se trouvent des métainformations sur le modèle :

- Name, le libellé du modèle qui sera affiché par Lazarus ;

- Author, votre nom en tant qu'auteur du modèle ;

- Description, une brève description du modèle.

IV-B - Implémentation des variables du modèle

À présent vient une étape légèrement fastidieuse : implémenter les variables dans tous les fichiers composant le projet. Il est possible d'automatiser ce processus à l'aide d'une petite application batch ou shell, mais nous allons tout détailler.

IV-B-1 - Renommage des fichiers

Important : tous les noms de variables doivent être précédés et suivis d'un double caractère de soulignement.

Ainsi, si une variable MAVARIABLE a été définie dans le fichier project.ini, elle doit être implémentée sous la forme __MAVARIABLE__ dans les noms de fichiers ou dans le contenu des fichiers.

Il en va de même pour les types : le type de la variable MAVARIABLE doit être implémenté sous la forme T__MAVARIABLE__.

Renommez tout d'abord les fichiers Unit1.pas et Unit1.lfm en __MAINUNIT__.pas et __MAINUNIT__.lfm.

Renommez ensuite tous les fichiers debugtemplate.* en __PROJNAME__.*. La variable PROJNAME est une variable définie par défaut qui représente le nom du nouveau projet lorsque celui-ci sera enregistré.

Ne touchez pas aux fichiers ufrmDebug.pas et ufrmDebug.lfm, ils garderont ces noms dans le nouveau projet.

IV-B-2 - Implémentation des variables dans les fichiers

Il s'agit à présent d'ouvrir chaque fichier composant le projet et d'y implémenter les variables du modèle.

Le fichier __MAINUNIT__.pas :

- toutes les occurrences de Form1 doivent être remplacées par __MAINFORM__ ;

- toutes les occurrences de TForm1 doivent être remplacées par T__MAINFORM__ ;

- MainMenu1 doit être remplacé par __MAINMENU__.

Voici le résultat :

unit __MAINUNIT__;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Menus, ufrmDebug;

type

{ T__MAINFORM__ }

T__MAINFORM__ = class(TForm)
mnuWindow : TMenuItem;
mnuWindowDebug : TMenuItem;
__MAINMENU__ : TMainMenu;
procedure mnuWindowDebugClick(Sender : TObject);
private

public

end;

var
__MAINFORM__ : T__MAINFORM__;

implementation
{$R *.lfm}
{ T__MAINFORM__ }
procedure T__MAINFORM__.mnuWindowDebugClick(Sender : TObject);
(* Shows or hides the debug window *)
begin
frmDebug.Visible := mnuWindowDebug.Checked;
end;
end.

Le fichier __MAINUNIT__.lfm :

- toutes les occurrences de Form1 doivent être remplacées par __MAINFORM__ ;

- TForm1 doit être remplacé par T__MAINFORM__ ;

- toutes les occurrences de MainMenu1 doivent être remplacées par __MAINMENU__.

Voici le résultat :

object __MAINFORM__: T__MAINFORM__
Left = 354
Height = 235
Top = 153
Width = 313
Caption = '__MAINFORM__'
DesignTimePPI = 94
Menu = __MAINMENU__
LCLVersion = '2.0.4.0'
object __MAINMENU__: TMainMenu
left = 6
object mnuWindow: TMenuItem
Caption = '&Window'
object mnuWindowDebug: TMenuItem
AutoCheck = True
Caption = '&Show log'
OnClick = mnuWindowDebugClick
end
end
end
end

Le fichier __PROJNAME__.lpr :

- l'identificateur du programme doit être remplacé par __PROJNAME__ ;

- dans la clause uses, Unit1 doit être remplacée par __MAINUNIT__ ;

- Form1 doit être remplacée par __MAINFORM__ ;

- TForm1 doit être remplacé par T__MAINFORM__.

Voici le résultat :

program __PROJNAME__;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, __MAINUNIT__, ufrmDebug;
{$R *.res}
begin
RequireDerivedFormResource := True;
Application.Scaled := True;
Application.Initialize;
Application.CreateForm(T__MAINFORM__, __MAINFORM__);
Application.CreateForm(TfrmDebug, frmDebug);
Application.Run;
end.

Le fichier __PROJNAME__.lpi :

- toutes les occurrences de debugtemplate doivent être remplacées par __PROJNAME__ ;

- à l'exception des balises <Unit1> et </Unit1>, toutes les occurrences de Unit1 doivent être remplacées par __MAINUNIT__ ;

- Form1 doit être remplacée par __MAINFORM__.

Voici le résultat :

<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="11"/>
<General>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="__PROJNAME__"/>
<Scaled Value="True"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<XPManifest>
<DpiAware Value="True"/>
</XPManifest>
<Icon Value="0"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="3">
<Unit0>
<Filename Value="__PROJNAME__.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
<Unit1>
<Filename Value="__MAINUNIT__.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="__MAINFORM__"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="__MAINUNIT__"/>
</Unit1>
<Unit2>
<Filename Value="ufrmdebug.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmDebug"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="ufrmDebug"/>
</Unit2>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="__PROJNAME__"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

Le fichier __PROJNAME__.lps :

- debugtemplate doit être remplacé par __PROJNAME__ ;

- à l'exception des balises <Unit1> et </Unit1>, toutes les occurrences de Unit1 doivent être remplacées par __MAINUNIT__ ;

- Form1 doit être remplacée par __MAINFORM__.

Et voici le résultat :

<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectSession>
<Version Value="11"/>
<BuildModes Active="Default"/>
<Units Count="3">
<Unit0>
<Filename Value="__PROJNAME__.lpr"/>
<IsPartOfProject Value="True"/>
<EditorIndex Value="-1"/>
<UsageCount Value="25"/>
</Unit0>
<Unit1>
<Filename Value="__MAINUNIT__.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="__MAINFORM__"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="__MAINUNIT__"/>
<CursorPos X="34" Y="41"/>
<UsageCount Value="25"/>
<Loaded Value="True"/>
<LoadedDesigner Value="True"/>
</Unit1>
<Unit2>
<Filename Value="ufrmdebug.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmDebug"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="ufrmDebug"/>
<IsVisibleTab Value="True"/>
<EditorIndex Value="1"/>
<CursorPos X="44" Y="33"/>
<UsageCount Value="25"/>
<Loaded Value="True"/>
<LoadedDesigner Value="True"/>
</Unit2>
</Units>
<JumpHistory Count="4" HistoryIndex="3">
<Position1>
<Filename Value="__MAINUNIT__.pas"/>
<Caret Line="41" Column="34"/>
</Position1>
<Position2>
<Filename Value="__MAINUNIT__.pas"/>
<Caret Line="42" Column="34"/>
</Position2>
<Position3>
<Filename Value="__MAINUNIT__.pas"/>
<Caret Line="41" Column="34"/>
</Position3>
<Position4>
<Filename Value="ufrmdebug.pas"/>
</Position4>
</JumpHistory>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0" ActiveMode=""/>
</RunParams>
</ProjectSession>
</CONFIG>

V - Utilisation du modèle

Notre modèle de projet étant finalisé, il est temps de tester son utilisation. Créez un nouveau projet, le nouveau modèle apparaît à présent dans la liste :

Vous voyez comment le contenu des clés Name et Description du fichier project.ini est utilisé par Lazarus.

Sélectionnez le nouveau modèle puis cliquez sur « OK ». Lazarus vous demande la valeur des différentes variables que nous avons implémentées :

- le nom du projet (PROJNAME) ;

- le nom de l'unité principale (MAINUNIT) ;

- le nom de la fiche principale (MAINFORM) ;

- le nom du menu principal (MAINMENU) ;

- ainsi que le répertoire du projet, qui est à créer.

Une fois que tous les champs sont complétés, cliquez sur « OK ». Vous disposez d'un nouveau projet possédant déjà une fenêtre de débogage ainsi qu'un menu principal :

Le gain de temps est évident !

VI - Remerciements

Je remercie Gilles Vasseur pour sa relecture technique et Claude Leloup pour ses corrections.