GENÈSE D'UN DICTIONNAIRE

Construction d'un lexique interactif avec Lazarus


précédentsommairesuivant

Onglet Balayage

Introduction

Notre projet comprend maintenant, outre une liste de mots impressionnante, une interface graphique qui permet de chercher et d'afficher n'importe quel élément de la liste.

Dans le présent chapitre seront abordées différentes techniques de navigation et d'affichage.

Environnement

Avant d'opérer les premières modifications, nous allons créer un nouveau répertoire, Lex3, et y recopier tous les fichiers du répertoire Lex2 : de cette façon, il sera toujours facile de retrouver notre projet dans un état antérieur.
Lancez Lazarus, dans le répertoire Lex3, ouvrez le projet Lex2.lpi et sauvegardez l'unité uLex2 sous le nom uLex3 ; de même pour le projet, qui prendra le nom de pLex3.pas. Acceptez la suppression des anciens fichiers. L'unité uDisque reste inchangée. Nettoyez le répertoire de tous les fichiers dont le nom inclut « Lex2 ».
Nous allons introduire de nouveaux composants : un curseur, un ListBox, deux boutons de navigation et un outil de zoom.
Dans l'état, notre interface graphique est insuffisante.
La fenêtre est extensible, en hauteur comme en largeur, ce qui dégagerait de l'espace pour de nombreux composants ; mais ce choix conduirait rapidement à une interface complexe donc difficilement utilisable.
Comment enrichir cette interface en conservant une ergonomie satisfaisante ? Rien de plus simple : cliquez sur la ligne en gris, à la hauteur et à droite de Page1. Clic droit pour faire apparaître le menu contextuel ; choisir Ajouter une page…
Dans la fenêtre Inspecteur d'objets, onglet Propriétés, ligne Caption, remplacez TabSheet1 par Balayage. Le nouvel espace de travail est disponible.

Curseur

Dans le menu Lazarus, onglet Common Controls, cliquez sur le deuxième composant (TTrackBar) ; cliquez maintenant sur l'interface graphique, onglet Balayage : le composant est installé. Placez-le en haut de la page et élargissez-le pour qu'il occupe toute sa largeur. Dans la fenêtre Inspecteur d'objets, fixez la propriété Max à 1000.

Image non disponible

Ajoutez un TLabel ; propriétés Font, Size, fixez la taille à 14.
Cliquez sur le composant Curseur ; dans l'inspecteur d'objets, onglet Événements, propriété OnMouseLeave, cliquez sur les trois points : l'éditeur de source fait apparaître une nouvelle procédure, intitulée TrackBar1MouseLeave.
Entre les directives begin et end recopiez les lignes suivantes :

 
Sélectionnez

iMot := Round(TrackBar1.Position*listeMots.Count/1000);
iMot := (iMot + listeMots.Count) mod  (listeMots.Count);
Label2.Caption:=AnsiToUTF8(listeMots[iMot]); 
  • la première ligne calcule la position du mot à partir de la position du curseur. Round retourne un nombre entier ;
  • la seconde ligne - nous l'avons déjà rencontrée - encadre le résultat dans l'intervalle disponible ;
  • enfin la troisième affiche le mot correspondant avec la conversion ANSI vers UTF-8, qui résout le problème d'affichage des caractères accentués.

Essayez… le balayage est extrêmement rapide, mais… reste grossier puisque pour une unité du curseur, iMot varie de 340 environ.

En effet, le curseur contient 1000 graduations et la liste environ 336 000 entrées.
Pour le vérifier, ajoutez un second TLabel sous le premier. Au code précédent, ajoutez la ligne
Label3.Caption:= IntToStr(iMot);
Relancez le programme et déplacez le curseur, non avec la souris, trop imprécise, mais avec les flèches droite/gauche du clavier.
Prenez soin de supprimer ce composant et la ligne de code avant de poursuivre.

ListBox

L'affichage mot par mot va être complété par la présentation des dix mots qui encadrent celui qui est affiché en titre, de façon à situer la consultation dans son contexte.
Dans l'onglet Standard du menu Lazarus, cliquez sur le composant TListBox situé juste après le TRadioButton. Cliquez ensuite sur notre interface graphique, dans l'onglet Balayage, et agencez l'ensemble sensiblement comme ci-dessous.

Image non disponible

Dans l'inspecteur d'objets, onglet Propriétés :

  • ligne BorderStyle, faites basculer la valeur bsSingle vers bsNone, pour faire disparaître le cadre ;
  • ligne Name, entrez AffListe.

Il est possible de charger la liste de mots complète dans le nouveau composant : des ascenseurs permettent théoriquement de naviguer d'une extrémité à l'autre de la liste.
Dans la pratique, il se révèle que le chargement est long, et la navigation laborieuse. Les plus courageux feront la vérification.
Pour la suite du projet, nous limiterons par conséquent l'affichage de la liste à un nombre réduit d'entrées de part et d'autre du mot affiché en titre.

Revenons au code pour modifier la procédure TrackBar1MouseLeave. Les instructions sont les suivantes :

 
Sélectionnez

procedure TForm1.TrackBar1MouseLeave(Sender: TObject);
var i, iMin, iMax : integer;
begin
   iMot := Round(TrackBar1.Position*listeMots.Count/1000);
   iMot := (iMot + listeMots.Count) mod  listeMots.Count;
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   AffListe.Clear;
   iMin := iMot - 5;
   if iMin < 0 then iMin :=0;
   iMax := iMot + 5;
   if iMax > listeMots.Count - 1 then iMax := listeMots.Count - 1 ;
   for i := iMin to iMax do
      AffListe.Items.Add(AnsiToUTF8(listeMots[i]));
   AffListe.Selected[5] := True;
end;        

Après avoir nettoyé la ListBox, le programme affiche tous les mots dont les index vont de iMot-5 à iMot+5, en assurant aux limites (iMin et iMax) de rester dans le cadre autorisé.
La dernière instruction sélectionne le mot titre avec surlignement graphique.
Avant de lancer l'exécution, il faut revenir en tête de l'unité uLex3 pour déclarer la nouvelle variable :

AffListe : TlistBox;

Lancez l'exécution. Un déplacement du curseur fait apparaître le mot correspondant à l'index choisi, accompagné, au-dessous, des mots les plus proches.

Image non disponible

Le mot « mimosa » figure en titre ; il est surligné dans la liste qui affiche les cinq mots précédents et les cinq mots suivants.
Avant de poursuivre le développement, nous allons structurer un peu le code en regroupant dans la procédure MAJBalayage toutes les instructions réservées à l'affichage.
Par ailleurs, le code utilisé révèle des difficultés en limites de listes. Les variables iMin et iMax deviennent inutiles si on envisage une permutation circulaire de la liste en utilisant la fonction Modulo. En outre, il est bon de synchroniser la position du curseur avec la nouvelle valeur de l'index iMot.
La procédure précédente est donc scindée en deux et modifiée en conséquence :

 
CacherSélectionnez

procedure TForm1.MAJBalayage;
var i : integer;
begin
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   TrackBar1.Position:= Round(iMot*1000/listeMots.Count);
   AffListe.Clear;
   for i := 0 to 10 do
       AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-5 + i + ListeMots.Count) mod              ListeMots.Count]));
   AffListe.Selected[5] := True;
end;    
 
procedure TForm1.TrackBar1MouseLeave(Sender: TObject);
begin
   iMot := Round(TrackBar1.Position*listeMots.Count/1000);
   iMot := (iMot + listeMots.Count) mod  listeMots.Count;
   MAJBalayage;
end;

Attention à la nouvelle procédure MAJBalayage : pour s'assurer que le compilateur la localise correctement, il convient de commencer par l'ajouter à la liste des procédures, avant le mot Implementation. En fin de saisie, un appui sur les touches Ctrl+Maj+C crée la procédure dans le corps de l'unité, où les instructions pourront être recopiées sans difficulté.

La deuxième procédure effectue les calculs et passe la main à la première.
Pour que l'onglet Balayage soit correctement renseigné dès le démarrage, on ajoute dans la procédure FormCreate l'instruction :

MAJBalayage;

Est-il possible de sélectionner un autre mot dans la liste ?
Il suffit pour cela d'utiliser l'événement OnClick du composant.
Quittez le mode exécution (carré rouge), sélectionnez le composant AffListe, et dans la fenêtre Inspecteur d'objets, onglet Événements, ligne OnClick, cliquez sur les trois points : la procédure adéquate est créée dans la fenêtre de code.
Entre les directives begin et end recopiez les lignes :

iMot := (iMot -5 + AffListe.ItemIndex + listeMots.Count) mod listeMots.Count;
MAJBalayage;

Passez en mode exécution (triangle vert), déplacez le curseur, cliquez sur le navigateur, cliquez sur un mot de la liste… Test sur les limites, action sur le curseur, utilisation des flèches du clavier… tout fonctionne correctement.
Le balayage commence à se construire. Mais… il reste grossier puisque le curseur a un pas de 340, et la liste affiche seulement 11 entrées. Un outil supplémentaire est indispensable pour se déplacer.

Navigateur

Dans le menu Lazarus, cliquez sur le petit carré rouge pour revenir en mode Édition.
Dans l'onglet Common Controls, cliquez sur le composant TUpDown (déjà vu), puis cliquez sur notre interface, onglet Balayage : le nouveau composant est installé.
Dans la fenêtre Inspecteur d'objets, onglet Propriétés, choisissez la valeur udHorizontal à la ligne Orientation.
Avec la souris, déplacez et modifiez la forme du composant pour obtenir sensiblement ceci :

Image non disponible

Dans la fenêtre Inspecteur d'objets, onglet Événements, ligne OnClick, cliquez sur les trois points.
Dans l'éditeur, entre les mots begin et end recopiez les instructions suivantes :

 
Sélectionnez

   if Button=btNext then Inc(iMot, UpDown2.Increment)
                    else Dec(iMot, UpDown2.Increment);
  iMot := (iMot + listeMots.Count) mod  (listeMots.Count);
   MAJBalayage;
  • la première ligne déclenche l'incrémentation de l'index iMot. La valeur de l'incrément est précisée par le paramètre UpDown2.Increment qui, par défaut, vaut 1 (voir les propriétés de l'objet) ;
  • la seconde replace la variable iMot dans l'intervalle autorisé ;
  • la troisième provoque la mise à jour de l'affichage.

Lancez l'exécution… Le balayage un par un fonctionne, mais reste réellement insuffisant : d'où la nécessité de rendre le pas (incrément) modifiable.

Zoom

Stoppez l'exécution pour revenir en mode Édition.
Nous allons ajouter un composant TGroupBox, qui se situe dans l'onglet Standard du menu Lazarus : clic sur ce composant, clic sur notre interface pour l'installer. Dans l'onglet Propriétés de ce composant, ligne Caption, écrivez le mot Zoom.
Dans le GroupBox(9), nous allons placer quatre composants TRadioButton, dont les propriétés Caption seront fixées respectivement à x1, x10, x100 et x1000. Sélectionnez le premier composant et portez, dans l'inspecteur d'objets, sa propriété Checked à True : il s'agit d'une valeur par défaut, modifiable évidemment pendant l'exécution.
À l'aide de la souris, modifiez la présentation pour obtenir une interface qui ressemble à celle qui est présentée :

Image non disponible

Sélectionnez le GroupBox. Dans l'onglet Événements, ligne OnMouseLeave, cliquez sur les trois points. Entre les mots begin et end recopiez les instructions suivantes :

 
Sélectionnez

if RadioButton1.Checked then UpDown2.Increment := 1
  else if RadioButton2.Checked then UpDown2.Increment := 10
  else if RadioButton3.Checked then UpDown2.Increment := 100
  else if RadioButton4.Checked then UpDown2.Increment := 1000; 

Lancez l'exécution, testez le curseur, le navigateur, le zoom, la sélection dans la liste…
Enfin une interface qui tient la route.

L'événement OnMouseLeave se déclenche lorsque le curseur de la souris quitte l'espace graphique réservé au composant GroupBox : pour modifier le pas de balayage, l'utilisateur doit cliquer sur l'un des RadioButton ; dès que la souris sera appelée pour une autre tâche (curseur, navigateur, liste…) la valeur de l'incrément sera mise à jour.

Conclusion

Nous disposons maintenant d'une interface graphique extensible à volonté, et d'un outil pratique pour consulter facilement notre liste de mots.
Il nous faut maintenant l'enrichir en permettant à l'utilisateur d'ajouter à chacune des entrées telle ou telle information qu'il jugerait utile.
Dans le prochain chapitre, nous verrons comment lier un mot à un nombre, un mot ou un texte, et enregistrer ce lien.

Notaire : arrive souvent au dernier acte.
Tristan Bernard

Dans l'état actuel du projet, l'unité uDisque reste inchangée. Voyons comment se présente l'unité uLex3.

1. La liste de mots est affichée avec les cinq mots qui précèdent le mot titre, et les cinq mots suivants. Pour fixer ce choix, nous créons la constante delta qui vaut 5. Les modifications de la taille seront ainsi facilitées en cas de besoin.

2. Au démarrage, il est préférable que l'onglet Balayage soit mis à jour correctement : il suffit pour cela d'ajouter l'instruction MAJBalayage dans la procédure FormCreate.

3. Par ailleurs, on retrouve fréquemment la variable ListeMots.Count qui compte le nombre d'entrées dans la liste principale. Pour alléger l'écriture et gagner en temps d'exécution, il sera judicieux de la remplacer par une variable globale, nMots par exemple : nous la mettrons en œuvre dans le chapitre suivant.

 
Sélectionnez

unit ulex3;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls,
  Graphics, Dialogs, StdCtrls, ComCtrls, uDisque;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit2: TEdit;
    RadioButton1: TRadioButton;
    RadioButton2: TRadioButton;
    RadioButton3: TRadioButton;
    RadioButton4: TRadioButton;
    Zoom: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    AffListe: TListBox;
    Memo1: TMemo;
    PageControl1: TPageControl;
    Page1: TTabSheet;
    TabSheet1: TTabSheet;
    TrackBar1: TTrackBar;
    UpDown1: TUpDown;
    UpDown2: TUpDown;
    procedure AffListeClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Recherche(rechMot: string);
    procedure TrackBar1MouseLeave(Sender: TObject);
    procedure UpDown1Click(Sender: TObject; Button: TUDBtnType);
    procedure MAJAffichage;
    procedure MAJBalayage;
    procedure UpDown2Click(Sender: TObject; Button: TUDBtnType);
    procedure ZoomMouseLeave(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
 
const delta=5;
 
var
  Form1: TForm1;
  listeMots: TstringList;
  iMot : integer;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.Button1Click(Sender: TObject);
begin
   listeMots.Free;
   Application.Terminate;
end;
 
procedure TForm1.AffListeClick(Sender: TObject);
begin
   iMot := (iMot -delta + AffListe.ItemIndex
             + listeMots.Count) mod listeMots.Count;
   MAJBalayage;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  Recherche(Edit2.Caption);
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  listeMots := TStringList.Create;
  LireFichier(listeMots);
  Memo1.Append('Premier mot : '+listeMots[0]);
  Memo1.Append('Dernier mot : '+listeMots[listeMots.Count-1]);
  iMot := 0;
  MAJAffichage;
  MAJBalayage;
end;
procedure TForm1.MAJBalayage;
var i : integer;
begin
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   TrackBar1.Position:= Round(iMot*1000/listeMots.Count);
   AffListe.Clear;
   for i := 0 to 10 do
       AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-delta + i
                          + ListeMots.Count) mod ListeMots.Count]));
   AffListe.Selected[delta] := True;
end;
 
procedure TForm1.UpDown2Click(Sender: TObject; Button: TUDBtnType);
begin
   if Button=btNext then Inc(iMot, UpDown2.Increment)
                    else Dec(iMot, UpDown2.Increment);
   iMot := (iMot + listeMots.Count) mod  (listeMots.Count);
   MAJBalayage;
end;
 
procedure TForm1.ZoomMouseLeave(Sender: TObject);
begin
   if RadioButton1.Checked then UpDown2.Increment := 1
   else if RadioButton2.Checked then UpDown2.Increment := 10
   else if RadioButton3.Checked then UpDown2.Increment := 100
   else if RadioButton4.Checked then UpDown2.Increment := 1000;
end;
 
 
procedure TForm1.TrackBar1MouseLeave(Sender: TObject);
begin
   iMot := Round(TrackBar1.Position*listeMots.Count/1000);
   iMot := (iMot + listeMots.Count) mod  listeMots.Count;
   MAJBalayage;
end;
 
procedure TForm1.Recherche(rechMot: string);
var iMot : integer;
begin
   iMot := listeMots.IndexOf(rechMot);
   if iMot >= 0 then Label1.Caption:= listeMots[iMot]
                else Label1.Caption:= 'échec';
   Memo1.Append('Index '+IntToStr(iMot));
end;
 
 
 
procedure TForm1.UpDown1Click(Sender: TObject; Button: TUDBtnType);
begin
   if Button=btNext then Inc(iMot)
                       else Dec(iMot);
   iMot := (iMot + listeMots.Count)   mod  (listeMots.Count);
   MAJAffichage;
end;
 
procedure TForm1.MAJAffichage;
begin
   Label1.Caption:=listeMots[iMot];
   Edit2.Caption:= '';
   Memo1.Append('Index '+IntToStr(iMot));
end;
 
end.

précédentsommairesuivant
Le composant TRadioGroup apporte une solution encore plus simple.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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 © 2014 dimanche2003. 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.