GENÈSE D'UN DICTIONNAIRE

Construction d'un lexique interactif avec Lazarus


précédentsommairesuivant

Premiers liens

Introduction

Notre liste de mots peut maintenant faire l'objet d'une consultation aisée, mais son utilité reste modeste puisque l'accès à un mot donné, aussi rapide soit-il, débouche sur… le néant ! Dans ce chapitre, nous allons voir comment attacher une information à ce mot, la stocker et la retrouver.

Environnement

Chapitre 5… Adoptons cet indice pour cette phase du projet : il n'y aura pas de répertoire Lex4.

Créons un répertoire Lex5 et recopions dans ce nouveau répertoire tous les fichiers du répertoire Lex3 utilisé précédemment. Pour éviter toute difficulté ultérieure, suivez la check-list :

  • ouvrir pLex3.lpi dans Lex5 avec Lazarus ;
  • enregistrer uLex3.pas sous le nom de uLex5.pas ;
  • accepter la suppression des références à uLex3.pas ;
  • enregistrer pLex3.pas sous le nom de pLex5.pas ;
  • renommer la fenêtre Form1 : Lex3 devient Lex5 ;
  • dans le répertoire Lex5 supprimer les anciens fichiers contenant la mention Lex3 ;
  • relancer le projet en ouvrant pLex5.lpi avec Lazarus ;
  • renommer l'interface graphique de Lex3 à Lex5.

Nous retrouvons le projet dans l'état où nous l'avions laissé, et les modifications que nous allons effectuer n'affecteront pas l'étape précédente conservée dans le répertoire Lex3.

Code

Pour alléger l'écriture du code, nous allons remplacer dans l'unité uLex5 toutes les occurrences de « listeMots.Count » par « nMots ».

Dans la procédure FormCreate, nous initions la variable par l'instruction :

nMots:= listeMots.Count ;

Nous complétons enfin la liste des variables globales (avant le bloc implementation) par la déclaration :

iMot, nMots : integer ;

Image non disponible

Un essai d'exécution (petit triangle vert) pour vérifier que tout va bien…

Image non disponible

Au plan graphique, un espace supplémentaire est nécessaire. Nous avons déjà vu que ce n'était pas compliqué.

Dans la fenêtre Lex5, clic droit sur la ligne en grisé, à droite de l'onglet Balayage. Dans le menu contextuel, choisissez Ajouter une page.

Dans la fenêtre Inspecteur d'objets, onglet Propriétés, remplacez TabSheet (ou Page, selon le composant utilisé) par Info, à la ligne Caption.

Le nouvel espace de travail est disponible.

Événement TrackBar1

Lors de la mise en place du composant TrackBar, qui donne un aperçu de la position du mot dans la liste principale, nous avons choisi d'utiliser l'événement onChange pour actualiser la relation entre la position du curseur et la valeur de l'index.

Il apparaît que des interférences peuvent perturber le balayage de la liste, notamment lors de l'utilisation d'un filtre.

Pour éviter ce type de problème dans la suite de notre projet, nous décidons d'abandonner cette procédure et d'utiliser à la place l'événement MouseUp, qui se déclenche tout simplement dès que le bouton de la souris est relâché à la fin du déplacement du curseur.

Voici la marche à suivre :

  • cliquer sur le composant TrackBar dans l'onglet Balayage ;
  • dans l'inspecteur d'objets, onglet Événements, ligne OnMouseUp, cliquer sur les trois points ;
  • dans l'éditeur de source, entre les mots begin et end, recopier les instructions qui figuraient dans la procédure TrackBar1Change :
 
Sélectionnez
procedure TForm1.TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   iMot := Round(TrackBar1.Position*nMots/1000);
   iMot := (iMot + nMots) mod  nMots;
   MAJBalayage;
end;

Toujours dans l'éditeur de source, supprimez complètement l'ancienne procédure, y compris sa déclaration avant implémentation. Dans l'inspecteur d'objets, onglet Événement, supprimez toute référence dans la ligne OnChange.

Un clic sur le petit triangle vert, vérifiez que votre curseur fonctionne bien…

Le conteneur de la liste de mots (StringList) possède une structure dotée de nombreuses propriétés dont certaines permettent de lier un élément de la liste à une information externe :

  • utilisation du couple Nom-Valeur ;
  • utilisation de la propriété Object.

Nous allons aborder leur mise en œuvre pour les évaluer.

Couple Nom-Valeur

C'est la solution la plus simple : elle est précâblée dans la liste, il est donc inutile d'entrer dans le cambouis. Pratique pour faire correspondre le nom d'une personne avec un numéro d'identification, une commune avec un code postal, etc.

Pour l'utiliser, il suffit d'affecter une chaîne quelconque à un élément de la liste.

Supposons que l'on s'intéresse au mot « a » et que l'on veuille le relier au mot « voyelle », il suffit d'écrire, en remplacement du premier mot :

'a=voyelle'

pour établir la liaison.

À chaque fois que l'on consultera le premier mot, le lien sera activé.

Il ne s'agit pas d'affecter une valeur à une variable.
Le signe « = » est le caractère de séparation par défaut. On peut en changer, par exemple avec « > ».
Dans ce cas, il faudra ajouter, dans la procédure de création de la liste, l'instruction :
listeMots.NameValueSeparator:='>' ;

Notons que la propriété Value est alphanumérique, ce qui permet d'introduire, au choix, des expressions numériques ou littérales.

Dans l'onglet Info, nous ajoutons successivement :

  • un Label (Propriété Font/Size portée à 14) ;
  • un Memo (Propriété ScrollBars portée à ssAutoVertical) ;
  • un Edit ;
  • et un bouton (Propriété Caption portée à Value).

Après divers agencements, l'onglet Info se présente sensiblement comme ceci :

Image non disponible

Valeur littérale

Un double-clic sur le bouton Value crée le squelette de la procédure événement dans l'éditeur de source. Entre les mots begin et end, on place l'instruction :

MAJInfo ;

 
Sélectionnez
procedure TForm1.Button3Click(Sender: TObject);
begin
  MAJInfo;
end;

Pour créer la procédure MAJInfo, nous commençons par la déclarer en tête de l'unité, dans la classe Tform, à la suite des autres procédures. Un appui sur les touches Ctrl+Maj+C crée la structure recherchée ; entre les mots begin et end, nous recopions :

 
Sélectionnez
procedure TForm1.MAJInfo;
begin
  listeMots[0] := 'a=voyelle';
  Label3.Caption:= listeMots.Names[0];
  Edit1.Caption:= listeMots.Values[listeMots.Names[0]];
  Memo2.Append('en mémoire : '+listeMots[0]);
end; 

La première instruction institue le lien voulu, en remplacement de la chaîne qui existait auparavant.

La seconde affiche la partie gauche de l'élément (propriété Names).

La troisième affiche la partie droite (propriété Values) ; il faut remarquer que l'index n'est plus numérique, c'est la partie gauche de l'élément qui constitue le nouvel index.

La quatrième ligne affiche l'élément au complet.

Image non disponible

Pour vérifier ces indications, cliquez sur le petit triangle vert, puis sur le bouton Value.

Utilisez le balayage dans l'onglet précédent : après un aller et retour sur le mot courant, on peut constater que la chaîne d'origine a bien été remplacée par un binôme.

Les autres chaînes n'ont pas été modifiées et s'affichent correctement. Dans l'onglet Info, le mot titre est bien « a » qui est bien relié au mot « voyelle ».

  1. La procédure MAJBalayage peut être modifiée pour que soit affichée en titre non pas l'enregistrement complet correspondant à l'index iMot, mais seulement sa partie gauche (Names[iMot]).
  2. L'enregistrement de la liste « enrichie » est aisé : il suffit d'utiliser l'instruction SaveToFile().
Mais attention à ne pas écraser la liste d'origine !
En cas de problème, remplacez le fichier corrompu par celui qui est resté dans le répertoire Lex3.

Valeur numérique

L'intérêt du lien entre le mot « a » et l'information « voyelle » est incontestable, mais… limité. D'autant qu'il n'est pas réversible : en l'état, si on consulte le mot « voyelle », aucune indication n'est donnée quant à son lien avec le mot « a ».

Et si, par excès de zèle, on décide d'établir la réciprocité au moyen de l'instruction 'voyelle=a', nous arrivons à une absurdité, puisque « a » n'est pas la seule voyelle, et que notre système ne permet pas l'établissement de liens multiples…

Un lien numérique est autrement plus puissant : le nombre éventuellement détecté peut servir d'index dans une table auxiliaire qui regrouperait une collection d'objets quelconques, par exemple des textes, des images, des sons, etc.

Nous allons voir comment procéder avec quatre mémos qui seront affectés respectivement aux mots « couleur », « teinte », « vert » et « rouge »(le désordre est intentionnel).

Saisie des mémos

Une consultation rapide de Wikipedia nous donne un peu de matière (totalement dépourvue d'humour, désolé…) pour nos mémos. Chaque texte va être stocké(10) dans une chaîne (string) et les quatre chaînes dans une liste (StringList). Pour commencer, il faut déclarer la nouvelle variable globale listeInfo : TStringList avant implementation :

Image non disponible

Nous créons la procédure regInfo qui se chargera de placer les chaînes en mémoire :

  • déclarez procedure regInfo ; avant implementation ;
  • appuyez sur Ctrl+Maj+C pour créer le squelette de la procédure ;
  • entre les mots clés begin et end recopiez les instructions :
 
Sélectionnez
procedure TForm1.regInfo;
begin
   listeInfo := TstringList.Create;
   //indice 0
   listeInfo.Add('Resa');
    //indice 1
   listeInfo.Add('La couleur est la perception subjective qu''a un animal '
     +'d''une ou plusieurs fréquences d''ondes lumineuses, '
     +'avec une (ou des) amplitude(s) donnée(s), au travers de sa vision oculaire.');
    //indice 2
   listeInfo.Add('Une teinte est la forme pure d''une couleur, '
     +'c''est-à-dire sans adjonction de blanc ou de noir qui permettent d''obtenir '
     +'ses nuances. Les teintes sont visualisées sur le pourtour d''une roue       chromatique.');
    //indice 3 
   listeInfo.Add('La perception de la couleur verte est évoquée par la lumière d''un'
     +' spectre dominé par de l''énergie avec une longueur d''onde d''environ 520-570 nm.');
    //indice 4
   listeInfo.Add('Le rouge est, sur le cercle chromatique, une des trois couleurs '
     +'(avec le jaune et le bleu) primaires. Pour la théorie ondulatoire de la lumière, '
     +'son rayonnement se situe entre l''orange et l''infrarouge du spectre électromagnétique.');
end; 

Les indices 1 à 4 correspondent aux chaînes associées aux mots « couleur », « teinte », « vert » et « rouge ».
Une lecture attentive permet de remarquer que l'indice 0 est utilisé implicitement pour adresser le mot « Resa »qui ne sert à rien… sinon à éviter les pièges de l'indice zéro.
Notez à nouveau que l'ordre des mots est indifférent.

Pour automatiser cette procédure, nous allons l'ajouter à la liste des tâches réalisées lors de la création de la fiche :

 
Sélectionnez
procedure TForm1.FormCreate(Sender: TObject);
begin
  listeMots := TStringList.Create;
  LireFichier(listeMots);
  nMots:= listeMots.Count;
  Memo1.Append('Premier mot : '+listeMots[0]);
  Memo1.Append('Dernier mot : '+listeMots[nMots-1]);
  iMot := 0;
  regInfo;
  MAJAffichage;
  MAJBalayage;
end;

Un clic sur le bouton Value doit réaliser les liens entre les mots-clés et les chaînes correspondantes :

 
Sélectionnez
procedure TForm1.Button3Click(Sender: TObject);
begin
   listeMots[listeMots.IndexOf('couleur')] := 'couleur=1';
   listeMots[listeMots.IndexOf('teinte')] := 'teinte=2';
   listeMots[listeMots.IndexOf('vert')] := 'vert=3';
   listeMots[listeMots.IndexOf('rouge')] := 'rouge=4';
end;

La procédure modifie la liste de mots (liste principale) en ajoutant aux mots choisis le numéro du texte les concernant, mots et numéros étant séparés par le signe « = ».

Cliquez sur le petit triangle vert pour exécuter le programme :

  • la création de la fenêtre provoque la mise en mémoire de quatre textes ;
  • un clic sur le bouton Value remplace les mots choisis par le couple mot=index.

Dans l'onglet Balayage, cherchez un des mots-clés, par exemple « teinte » et vérifiez que le mémo correspondant s'affiche.

Affichage

L'onglet Balayage permet d'atteindre n'importe quel mot de la liste. Nous allons le compléter pour qu'il affiche, à côté du mot, le mémo qui lui est affecté, s'il existe.

Dans le menu Lazarus, cliquez sur le composant TMemo, puis cliquez sur l'onglet Balayage et placez le composant à droite du zoom ; dans l'onglet Propriétés de l'Inspecteur d'objets, ligne ScrollBars, choisissez ssAutoVertical pour faire apparaître un ascenseur.

Les procédures MAJBalayage et MAJInfo sont modifiées comme suit :

 
Sélectionnez
procedure TForm1.MAJBalayage;
var i : integer;
begin
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   Label3.Caption:= Label2.Caption;
   TrackBar1.Position:= Round(iMot*1000/nMots);
   AffListe.Clear;
   for i := 0 to 10 do
       AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-5 + i
                         + nMots) mod nMots]));
   AffListe.Selected[5] := True;
   Memo3.Clear;
   if (Pos('=', listeMots[iMot])>0) then MAJInfo;
end; 
 
procedure TForm1.MAJInfo;
begin
  //listeMots[0] := 'a=voyelle';
  Label3.Caption:= listeMots.Names[iMot];
  Edit1.Caption:= listeMots.Values[Label3.Caption];
  Memo2.Clear;
  Memo2.Append(listeInfo[StrToInt(Edit1.Caption)]);
  Label2.Caption:= listeMots.Names[iMot];
  Memo3.Append(listeInfo[StrToInt(Edit1.Caption)]);
end; 

Le programme est opérationnel.

Image non disponible

Pour le vérifier, clic sur le petit triangle vert. Dans l'onglet Info, clic sur le bouton Value pour lier les mémos à la liste principale.

Dans l'onglet Balayage, actionnez le curseur et les flèches de navigation, en utilisant le zoom si nécessaire, et vérifiez que, pour chacun des mots « couleur », « teinte », « vert » et « rouge », le mémo correspondant s'affiche.

Navigation

La recherche de nos mots « renseignés » se révèle fastidieuse : la liste principale est immense et il faut beaucoup de manœuvres pour les retrouver.

Un filtre va régler ce problème.

Dans l'onglet Balayage, on ajoute un CheckBox, propriété Caption modifiée avec « Filtre ».

Image non disponible

Dans l'onglet Événements, ligne OnChange, cliquez sur les trois points pour provoquer la création de la procédure CheckBox1Change, où l'on insérera les lignes :

if CheckBox1.Checked then CheckBox1.Caption := 'Avec filtre'

else CheckBox1.Caption := 'Sans filtre';

La procédure UpDown2 est modifiée pour traiter le cas où la case est cochée (filtre actif) :

 
Sélectionnez
procedure TForm1.UpDown2Click(Sender: TObject; Button: TUDBtnType);
begin
   if CheckBox1.Checked then
      repeat
        inc(iMot);
      until (iMot=nMots) or (Pos('=', listeMots[iMot])>0)
   else
      if Button=btNext then Inc(iMot, UpDown2.Increment)
                         else Dec(iMot, UpDown2.Increment);
   iMot := (iMot + nMots) mod  nMots;
   MAJBalayage;
end;

Si une flèche est cliquée alors que le filtre est actif, la liste de mots est balayée automatiquement jusqu'à ce qu'un mot « renseigné » (décelé par la présence du signe « = ») apparaisse.

Relancez le programme. Dans l'onglet Info, cliquez sur le bouton Value pour établir les liens. Dans l'onglet Balayage, activez le filtre et… naviguez.

Image non disponible

Développements complémentaires

Chaque mot peut maintenant être relié au texte de son choix.

Par exemple, le mot « rouge » est présenté avec une notice immédiatement accessible.

Mais il reste à faire pour que le programme soit réellement opérationnel : il manque notamment les procédures d'édition (modification, suppression, ajout, enregistrement) des mémos.

Notez que la suppression d'un lien au niveau de la liste principale devra être suivie par sa suppression du mémo correspondant dans la liste secondaire.

Ces étapes sont plus simples, et nous aurons l'occasion d'y revenir.

Propriété Object

Pour simplifier, nous dirons que cette propriété fonctionne comme la précédente, sous réserve de remplacer l'affectation

listeMots[listeMots.IndexOf('rouge')] := 'rouge=4' ;

par l'instruction

listeMots.Objects[listeMots.IndexOf('rouge')]:= Tobject(4) ;

Dans le premier cas, le lien s'établit sous forme d'une chaîne de caractères qui remplace (complète) la chaîne de caractères existante ; dans le second, le lien est réalisé par un pointeur : la chaîne de caractères reste inchangée mais se trouve liée à un « objet ». Là débute un aspect de l'informatique un peu plus complexe, celui des pointeurs(11). Notre projet a pour ambition de rendre toutes les opérations facilement accessibles, donc l'expérimentation restera succincte.

Rappelons, pour commencer, que le lien peut viser n'importe quel type d'information, que ce soit des images, du son, des fichiers PDF… Ici, nous reprendrons l'adressage de texte.

Saisie des mémos

Sur l'interface graphique, onglet Info, nous ajoutons un bouton, propriété Caption portée à Object.

Image non disponible

Un double-clic sur ce composant crée la structure de la procédure :

 
Sélectionnez
procedure TForm1.Button4Click(Sender: TObject);
begin
   listeMots.Objects[listeMots.IndexOf('couleur')] := TObject(1);
   listeMots.Objects[listeMots.IndexOf('teinte')] := TObject(2);
   listeMots.Objects[listeMots.IndexOf('vert')] := TObject(3);
   listeMots.Objects[listeMots.IndexOf('rouge')] := TObject(4);
end;

En somme, c'est la même procédure que pour le bouton Value, avec les modifications propres aux liens-objets.

Notez encore que l'affectation des objets se fait sans tenir compte de l'ordre alphabétique des mots.

Affichage

Les procédures MAJBalayage et MAJInfo sont modifiées comme suit :

 
Sélectionnez
procedure TForm1.MAJBalayage;
var i : integer;
begin
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   Label3.Caption:= Label2.Caption;
   //Label2.Caption:=AnsiToUTF8(listeMots.Names[iMot]);
   TrackBar1.Position:= Round(iMot*1000/nMots);
   AffListe.Clear;
   for i := 0 to 10 do
       AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-5 + i + nMots) mod nMots]));
   AffListe.Selected[5] := True;   
   Memo3.Clear;
   if (Pos('=', listeMots[iMot])>0) then MAJInfo;
   if  listeMots.Objects[iMot]<> nil then MAJInfo;
end;  
 
procedure TForm1.MAJInfo;
begin
   Label3.Caption:= listeMots.Names[iMot];
   //Edit1.Caption:= listeMots.Values[Label3.Caption];
   //Memo2.Clear;
   //Memo2.Append(listeInfo[StrToInt(Edit1.Caption)]);
   //Label2.Caption:= listeMots.Names[iMot];
   //Memo3.Append(listeInfo[StrToInt(Edit1.Caption)]);
   Memo3.Append(listeInfo[integer(listeMots.Objects[iMot])]); 
end;

Notez la dernière ligne de la première procédure : elle contrôle l'existence d'un objet lié à la chaîne d'index iMot. S'il n'y a pas d'objet, le contrôle rencontre la valeur nil, c'est-à-dire rien. Dans le cas contraire, elle déclenche la procédure d'affichage.

Celle-ci est complétée par une ligne qui calcule la valeur de l'index (cette valeur est le contenu de l'objet) et affiche le texte correspondant à cet index.

Les lignes concernant les couples name-value sont désactivées car les deux méthodes sont - en l'état - incompatibles.

Pour essayer, un clic sur le petit triangle vert dans le menu Lazarus. Un clic sur le bouton Object dans l'onglet Info, et navigation dans l'onglet Balayage. Les mémos s'affichent.

Filtre

L'exécution montre que la recherche et l'affichage des mémos sont aussi fastidieux qu'avec la méthode précédente, d'autant que le filtre se révèle sans effet.

Pour le réactiver, il suffit de rajouter un contrôle dans la procédure UpDown2Click :

 
Sélectionnez
procedure TForm1.UpDown2Click(Sender: TObject; Button: TUDBtnType);
begin
   if CheckBox1.Checked then
      repeat
        inc(iMot);
      until (iMot=nMots) or (Pos('=', listeMots[iMot])>0)
                                  or (listeMots.Objects[iMot]<> nil)
   else
      if Button=btNext then Inc(iMot, UpDown2.Increment)
                         else Dec(iMot, UpDown2.Increment);
   iMot := (iMot + nMots) mod  nMots;
   MAJBalayage;
end;

Si la case Filtre est cochée, le balayage de la liste se poursuit automatiquement jusqu'à ce que le mot soit lié à un objet.

Essayez…

Compatibilité

A priori, rien n'indique qu'une méthode neutralise l'autre. Le mieux est quand même de le vérifier.

Pour cela, nous allons doubler la mise en mémoire de texte et l'affichage :

Image non disponible

  • dans l'onglet Balayage, nous ajoutons un memo avec ascenseur vertical ;
  • nous ajoutons la variable globale qui recevra les textes objets :

listeObj : TstringList ;

  • dans la procédure FormCreate nous ajoutons l'instruction :

regObj ;

  • la procédure regObj(12) placera en mémoire les textes à lier sous forme d'objets :
 
Sélectionnez
procedure TForm1.regObj;
begin
   listeObj := TStringList.Create;
   listeObj.Add('ResaObj'); //index 0
   listeObj.Add('Ceci est un complément d''information pour le mot couleur');
   listeObj.Add('Voici une information qui sera liée au mot violet');
end; 
  • Et nous scindons l'affichage en deux parties dédiées, ce qui fait intervenir trois procédures(13) :
 
Sélectionnez
procedure TForm1.MAJBalayage;
var i : integer;
begin
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   Label3.Caption:= Label2.Caption;
   //Label2.Caption:=AnsiToUTF8(listeMots.Names[iMot]);
   TrackBar1.Position:= Round(iMot*1000/nMots);
   AffListe.Clear;
   for i := 0 to 10 do
       AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-delta + i
                         + nMots) mod nMots]));
   AffListe.Selected[delta] := True;
   Memo3.Clear;
   Memo4.Clear;
   if (Pos('=', listeMots[iMot])>0) then MAJInfo;
   if  listeMots.Objects[iMot]<> nil then MAJObj;
end;
 
procedure TForm1.MAJInfo;
begin
   Label3.Caption:= listeMots.Names[iMot];
   Edit1.Caption:= listeMots.Values[Label3.Caption];
   Memo2.Clear;
   Memo2.Append(listeInfo[StrToInt(Edit1.Caption)]);
   Label2.Caption:= listeMots.Names[iMot];
   Memo3.Append(listeInfo[StrToInt(Edit1.Caption)]);
   //Memo3.Append(listeInfo[integer(listeMots.Objects[iMot])]);
end;
 
 
procedure TForm1.MAJObj;
begin
   Memo4.Append(listeObj[integer(listeMots.Objects[iMot])]);
end;

Enfin, les instructions propres au bouton Object doivent être modifiées :

 
Sélectionnez
procedure TForm1.Button4Click(Sender: TObject);
begin
   listeMots.Objects[listeMots.IndexOf('couleur')] := TObject(1);
   listeMots.Objects[listeMots.IndexOf('violet')] := TObject(2);
   //listeMots.Objects[listeMots.IndexOf('vert')] := TObject(3);
   //listeMots.Objects[listeMots.IndexOf('rouge')] := TObject(4);
end;

Un clic sur le petit triangle vert pour lancer l'exécution. Dans l'onglet Info, cliquez sur le bouton Object pour établir les liens entre les mots et les textes-objets. Dans l'onglet Balayage, actionnez le filtre et balayez : les textes apparaissent dans le nouveau mémo, pour les mots « couleur » et « violet ».

Image non disponible

Donc le fonctionnement est correct pour la propriété Object.

Revenez sur l'onglet Info, cliquez sur le bouton Value. Reprenez le balayage : les textes-values s'affichent correctement, accompagnés des textes-objets : les liens sont donc validés sous les deux formes simultanément.

Mais…

Arrêtez le programme, relancez-le et chargez les textes dans l'ordre inverse : Value d'abord, Object ensuite ; cette fois, il y a une erreur :

Image non disponible

Pourquoi ?

La réponse est très simple : nous avons vu que le lien Value s'établissait en complétant la chaîne d'origine par un signe « = » suivi d'une autre chaîne. Donc lorsque le lien Object va chercher sa cible, il ne pourra pas la trouver puisqu'elle aura été modifiée.

Ce problème va-t-il compromettre définitivement la compatibilité entre les deux méthodes ?

Nous nous souvenons que la chaîne d'origine se retrouve sous forme de Value. Donc nous allons tout simplement remplacer la fonction

listeMots.IndexOf()

par la fonction plus élaborée

chercheValue()

qui peut s'écrire ainsi(14) :

 
Sélectionnez
function TForm1.chercheValue(motAchercher: string): integer;
var i : integer;
begin
  i := 0;
  chercheValue := listeMots.IndexOf(motAchercher);
  if chercheValue<0 then
  repeat
    inc(i)
  until (i = nMots) or (listeMots.Names[i] = motAchercher);
  if listeMots.Names[i]=motAchercher then chercheValue := i;
end;

Nous corrigeons la procédure qui établit les liens-objets :

 
Sélectionnez
procedure TForm1.Button4Click(Sender: TObject);
begin
   listeMots.Objects[chercheValue('couleur')] := TObject(1);
   listeMots.Objects[chercheValue('violet')] := TObject(2);
end;

Relancez l'exécution en chargeant (onglet Info) les liens Value, puis les liens Object. Dans l'onglet Balayage, actionnez le filtre et naviguez…

Impeccable !

Nos liens Value et Object cohabitent parfaitement.

Conclusion

Nous disposons maintenant de deux méthodes pour attacher à chaque mot toutes sortes d'informations que nous pourrions désirer.

En gros, on peut dire que les deux méthodes sont équivalentes en termes de mise en œuvre.

  • La première surcharge la liste principale avec une chaîne de deux à sept octets (depuis '=1' jusqu'à '=900000') si le nombre de liens reste inférieur à un million(15). Pour déterminer la partie droite de la chaîne, il faut indiquer la partie gauche, qui est une chaîne de caractères.
  • La seconde utilise pour chaque lien un pointeur, qui occupe quatre octets. Les affichages sont simplifiés puisque le signe « = » et l'index n'apparaissent pas dans la liste principale. Pour déterminer le pointeur, il faut indiquer la position de la chaîne dans la liste principale, qui est un entier.

Resterait à analyser les performances au niveau des temps d'accès et occupation de la mémoire, sans compter les compléments d'enregistrement et d'édition déjà mentionnés. Mais…

Mais il faut reconnaître que l'avancée est bien mince au regard des performances offertes par les dictionnaires en ligne : chacun de nos mots peut être relié à une information et une seule (ou deux en combinant les deux méthodes), ce qui largement insuffisant.

Il est donc nécessaire de sortir des solutions «  précâblées  », et c'est ce que nous ferons dans le prochain chapitre.

Âge : ne reste pas longtemps ingrat.
Tristan Bernard

Pour l'instant, l'unité uLex5 se présente comme suit :

 
Sélectionnez
unit uLex5;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls,
  Graphics, Dialogs, StdCtrls, ComCtrls, uDisque;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    CheckBox1: TCheckBox;
    Edit1: TEdit;
    Edit2: TEdit;
    Label3: TLabel;
    Memo2: TMemo;
    Memo3: TMemo;
    Memo4: TMemo;
    RadioButton1: TRadioButton;
    RadioButton2: TRadioButton;
    RadioButton3: TRadioButton;
    RadioButton4: TRadioButton;
    TabSheet2: TTabSheet;
    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 Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure CheckBox1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Recherche(rechMot: string);
    procedure TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure UpDown1Click(Sender: TObject; Button: TUDBtnType);
    procedure MAJAffichage;
    procedure MAJBalayage;
    procedure UpDown2Click(Sender: TObject; Button: TUDBtnType);
    procedure ZoomMouseLeave(Sender: TObject);
    procedure MAJInfo;
    procedure regInfo;
    procedure regObj;
    procedure MAJObj;
    function chercheValue(motAchercher : string) : integer;
  private
    { private declarations }
  public
    { public declarations }
  end;
 
const delta=5;
 
var
  Form1: TForm1;
  listeMots, listeInfo, listeObj : TstringList;
  iMot, nMots : 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 + nMots) mod nMots;
   MAJBalayage;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  Recherche(Edit2.Caption);
end;
 
procedure TForm1.Button3Click(Sender: TObject);
begin
   //MAJInfo;
   listeMots[listeMots.IndexOf('couleur')] := 'couleur=1';
   listeMots[listeMots.IndexOf('teinte')] := 'teinte=2';
   listeMots[listeMots.IndexOf('vert')] := 'vert=3';
   listeMots[listeMots.IndexOf('rouge')] := 'rouge=4';
end;
 
procedure TForm1.Button4Click(Sender: TObject);
begin
   listeMots.Objects[chercheValue('couleur')] := TObject(1);
   listeMots.Objects[chercheValue('violet')] := TObject(2);
end;
 
procedure TForm1.CheckBox1Change(Sender: TObject);
begin
   if CheckBox1.Checked then CheckBox1.Caption := 'Avec filtre'
   else CheckBox1.Caption := 'Sans filtre';
end;
 
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  listeMots := TStringList.Create;
  LireFichier(listeMots);
  nMots:= listeMots.Count;
  Memo1.Append('Premier mot : '+listeMots[0]);
  Memo1.Append('Dernier mot : '+listeMots[nMots-1]);
  iMot := 0;
  regInfo;
  regObj;
  MAJAffichage;
  MAJBalayage;
end;
procedure TForm1.MAJBalayage;
var i : integer;
begin
   Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
   Label3.Caption:= Label2.Caption;
   //Label2.Caption:=AnsiToUTF8(listeMots.Names[iMot]);
   TrackBar1.Position:= Round(iMot*1000/nMots);
   AffListe.Clear;
   for i := 0 to 10 do
       AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-delta + i
                         + nMots) mod nMots]));
   AffListe.Selected[delta] := True;
   Memo3.Clear;
   Memo4.Clear;
   if (Pos('=', listeMots[iMot])>0) then MAJInfo;
   if  listeMots.Objects[iMot]<> nil then MAJObj;
end;
 
procedure TForm1.UpDown2Click(Sender: TObject; Button: TUDBtnType);
begin
   if CheckBox1.Checked then
      repeat
        inc(iMot);
      until (iMot=nMots) or (Pos('=', listeMots[iMot])>0)
                         or (listeMots.Objects[iMot]<> nil)
   else
      if Button=btNext then Inc(iMot, UpDown2.Increment)
                         else Dec(iMot, UpDown2.Increment);
   iMot := (iMot + nMots) mod  nMots;
   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.MAJInfo;
begin
   Label3.Caption:= listeMots.Names[iMot];
   Edit1.Caption:= listeMots.Values[Label3.Caption];
   Memo2.Clear;
   Memo2.Append(listeInfo[StrToInt(Edit1.Caption)]);
   Label2.Caption:= listeMots.Names[iMot];
   Memo3.Append(listeInfo[StrToInt(Edit1.Caption)]);
   //Memo3.Append(listeInfo[integer(listeMots.Objects[iMot])]);
end;
 
procedure TForm1.regInfo;
begin
   listeInfo := TStringList.Create;
   listeInfo.Add('Resa');
   listeInfo.Add('La couleur est la perception subjective qu''a un animal '
     +'d''une ou plusieurs fréquences d''ondes lumineuses, '
     +'avec une (ou des) amplitude(s) donnée(s), au travers de sa vision oculaire.');
   listeInfo.Add('Une teinte est la forme pure d''une couleur, '
     +'c''est-à-dire sans adjonction de blanc ou de noir qui permettent d''obtenir '
     +'ses nuances. Les teintes sont visualisées sur le pourtour d''une roue chromatique.');
   listeInfo.Add('La perception de la couleur verte est évoquée par la lumière d''un'
     +' spectre dominé par de l''énergie avec une longueur d''onde d''environ 520-570 nm.');
   listeInfo.Add('Le rouge est, sur le cercle chromatique, une des trois couleurs '
     +'(avec le jaune et le bleu) primaires. Pour la théorie ondulatoire de la lumière, '
     +'son rayonnement se situe entre l''orange et l''infrarouge du spectre électromagnétique.');
end;
 
procedure TForm1.regObj;
begin
   listeObj := TStringList.Create;
   listeObj.Add('ResaObj');
   listeObj.Add('Ceci est un complément d''information pour le mot couleur');
   listeObj.Add('Voici une information qui sera liée au mot violet');
end;
 
procedure TForm1.MAJObj;
begin
   Memo4.Append(listeObj[integer(listeMots.Objects[iMot])]);
end;
 
function TForm1.chercheValue(motAchercher: string): integer;
var i : integer;
begin
  i := 0;
  chercheValue := listeMots.IndexOf(motAchercher);
  if chercheValue<0 then
  repeat
    inc(i)
  until (i = nMots) or (listeMots.Names[i] = motAChercher);
  if listeMots.Names[i]=motAChercher then chercheValue := i;
end;
 
 
procedure TForm1.TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   iMot := Round(TrackBar1.Position*nMots/1000);
   iMot := (iMot + nMots) mod  nMots;
   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 + nMots   mod  nMots;
   MAJAffichage;
end;
 
procedure TForm1.MAJAffichage;
begin
   Label1.Caption:=listeMots[iMot];
   Edit2.Caption:= '';
   Memo1.Append('Index '+IntToStr(iMot));
end;
 
end.

précédentsommairesuivant
Cette méthode évite une saisie fastidieuse.
Les index utilisés jusqu'à présent donnent accès à un objet stocké dans une liste ; les pointeurs donnent accès à un objet situé à une position donnée de la mémoire vive.
À déclarer avant implementation : voir regInfo.
La nouvelle procédure MAJObj doit faire l'objet d'une déclaration avant implementation.
Ne pas oublier la déclaration avant implementation.
C'est là où la numération hexadécimale montre sa supériorité, mais nous resterons dans la simplicité.

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.