INFORMATIQUE

TP 5

Gestion des résultats d'un championnat


1. Objectifs

Chaque année, de très nombreux championnats sont organisés. Ces championnats fonctionnent sur un nombre d'équipes variable (de 6 ou 7 jusqu'à une vingtaine). Ils concernent aussi bien le football, le basket, le hand, le volley, le tennis et, pourquoi pas, le billard, les mots croisés, le scrabble, le jeu de billes ... Généralement les équipes se rencontrent par matches aller et retour et le classement évolue au fur et à mesure que le championnat se déroule.

Dans notre projet, nous allons pouvoir :

2. Mise en place de la fiche principale

La fiche principale doit ressembler à ceci :
Les propriétés de la fiche sont les suivantes :

Placer sur la fiche un composant StringGrid qui doit occuper la majeure partie de la fiche. Ses propriétés sont les suivantes :

Placer également 6 boutons en bas de la fiche, nommés respectivement btOuvrir, btEnregistrer, btNouveau, btImprimer, btClassement et btQuitter. Préciser les propriétés Caption de ces 6 boutons comme indiqué ci-dessus. Mettre les propriétés Enabled des boutons btEnregistrer, btImprimer et btClassement à False car ces boutons ne seront pas accessibles au lancement de l'application.

Choisir le bouton btQuitter et, dans l'inspecteur d'objets, onglet Evénements, faire un double-clic dans la zone de saisie de l'événement OnClick. Taper alors le texte habituel permettant de quitter une application (nous verrons plus loin comment alerter l'utilisateur s'il a oublié d'enregistrer) :

procedure TfmChampionnat.btQuitterClick(Sender: TObject);
begin
   Close;
end;

Enregistrer tout :

Lancer l'exécution de l'application pour vérifier que le bouton Quitter fonctionne bien.

3. Mise en place de la fiche Nouveau

Cliquer sur le bouton " Nouvelle fiche " qui permettra de définir les paramètres d'un nouveau championnat. Voici l'allure générale de cette nouvelle fiche :

Propriétés des différents composants :

fiche name fmNouveau
caption Nouveau championnat
BorderStyle bsDialog
StringGrid name grNomsDesEquipes
ColCount 2
RowCount 21
Options ne modifier que les options goDrawFocusSelected et goEditing qui prennent pour valeur True
bouton OK name btOK
caption &OK
ModalResult mrOK
bouton annuler name btAnnuler
name &Annuler
ModalResult mrCancel

Il reste à mettre en place la partie droite de la fiche, ce qui est un peu plus compliqué.

4. Réponse à un clic de souris sur le bouton Nouveau

Il faut d'abord ajouter, dans un paragraphe Uses, le nom de la fiche que l'on vient de créer. Pour cela, on tapera le texte suivant, à la position indiquée ci-dessous en gras :

var
   fmChampionnat: TfmChampionnat;

   nbEquipes : integer; // nombre d'équipes limité à 20

implementation

uses uNouveau;

{$R *.DFM}

procedure TfmChampionnat.btQuitterClick(Sender: TObject);
begin
   Close;
end;

Il faut ensuite sélectionner le bouton btNouveau, puis dans la partie Evénement de l'inspecteur d'objets, choisir l'événement OnClick :

procedure TfmChampionnat.btNouveauClick(Sender: TObject);
var n : integer;
begin
   if fmNouveau.showmodal = mrOK then
   begin
      n:=0; //on commence par calculer le nombre d'équipes dans le championnat
      while fmNouveau.grNomsDesEquipes.Cells[1,n+1]<>'' do inc(n);
      nbEquipes:=n;

      //on met à jour les propriétés RowCount et ColCount du copmosant grResultats
      grResultats.RowCount:=nbEquipes+1;
      grResultats.ColCount:=nbEquipes+1;

      //On remplit la 1ère ligne et la 1ère colonne avec les noms des équipes
      for n:=1 to nbEquipes do
      begin
         grResultats.Cells[0,n]:=fmNouveau.grNomsDesEquipes.cells[1,n];
         grResultats.cells[n,0]:=fmNouveau.grNomsDesEquipes.cells[1,n];
      end;
      grResultats.Visible:=true;   //on affiche la grille des résultats
      grResultats.col:=1;          //on met le curseur dans la cellule(1,2)
      grResultats.row:=2;
      grResultats.SetFocus;        //on met la focalisation sur la grille
      btOuvrir.Caption:='&Fermer'; //on change le libellé du bouton Ouvrir
      btClassement.Enabled:=true;  //le bouton Classement devient actif
      btEnregistrer.Enabled:=true; //le bouton enregistrer devient actif
      btImprimer.Enabled :=true ;  //le bouton imprimer devient actif
      btNouveau.Enabled:=false;    //le bouton Nouveau n'est plus actif
   end;
end;

5. Contrôle de l'affichage de la grille des résultats

Cette grille va servir à saisir les résultats de tous les matches (aller et retour) du championnat. À l'intersection de la ligne i et de la colonne j, on considère qu'il s'agit du résultat du match de l'équipe i qui reçoit l'équipe j. Les résultats des matches à domicile de l'équipe i seront donc saisis sur la ligne i et les résultats des matches à l'extérieur de cette même équipe i seront donc dans la colonne i.

Il va de soi que chacune des équipes doit rencontrer toutes les autres et que, à la fin du championnat, toutes les cellules de la grille seront remplies sauf celles qui sont situées sur la diagonale car une équipe ne joue jamais contre elle-même ! Il faut donc empêcher la saisie dans les cases de coordonnées (i, i) et mettre ces cases en gris pour montrer qu'elles sont inaccessibles.

De plus, pour améliorer l'affichage, il faut que les scores soient affichés au milieu de chaque case. Un score sera obligatoirement constitué de 2 entiers séparés par le signe -, comme par exemple 4-2. Dans cet exemple, 4 est le nombre de " buts " marqués par l'équipe qui reçoit et 2 est le nombre de " buts " concédés (toujours par l'équipe qui reçoit).

Pour réaliser tout ça, choisir l'événement OnDrawCell du composant grResultats et taper le code suivant, assez complexe :

procedure TfmChampionnat.grResultatsDrawCell(Sender: TObject; ACol,
                ARow: Integer; Rect: TRect; State: TGridDrawState);
var ch : string;
begin
   with grResultats.Canvas do
   begin
      if (Acol=ARow) and (ACol>0) then
      begin
         brush.Style:=bsSolid;
         if gdSelected in State then brush.Color:=clNavy
                                else brush.Color:=clSilver;
         Rectangle(rect.left-1,rect.top-1,rect.right+1,rect.bottom+1);
         brush.style:=bsClear;
      end;
      if (ACol<>ARow)and(ACol>0)and(ARow>0) then
      begin
         ch:=grResultats.cells[ACol,ARow];
         brush.Style:=bsSolid;
         if gdSelected in State then brush.Color:=clNavy
                                else brush.Color:=clWhite;
         Rectangle(rect.left-1,rect.top-1,rect.right+1,rect.bottom+1);
         brush.style:=bsClear;
         textout((rect.left+rect.right-textwidth(ch))div 2,
                 (rect.top+rect.bottom-textheight(ch))div 2,ch);
      end;
   end;
end;

Il faut maintenant faire en sorte que la saisie ne soit pas possible sur les cases situées sur la diagonale. Pour cela, on peut utiliser l'événement OnClick du composant grResultats et taper le code suivant :

procedure TfmChampionnat.grResultatsClick(Sender: TObject);
begin
   with grResultats do
   begin
      if Col=Row then Options:=Options-[goEditing,goDrawFocusSelected]
                 else Options:=Options+[goEditing,goDrawFocusSelected];
   end;
end;

Il faut aussi qu'en cas de modification de la grille de saisie, le bouton Enregistrer devienne actif . On utilise pour cela l'événement OnKeyPressed du composant grResultats :

procedure TfmChampionnat.grResultatsKeyPress(Sender: TObject; var Key: Char);
begin
   btEnregistrer.Enabled:=true;
end;

Le bouton Enregistrer ne deviendra inactif que lorsqu'on au aura cliqué dessus et qu'on aura enregistré les résultats sur le disque. C'est ce que nous allons faire maintenant, mais auparavant :

6. Enregistrement du tableau des résultats

Nous n'enregistrerons pas le tableau contenant le classement des équipes (que nous ferons plus loin) mais seulement le tableau des résultats. Le tableau du classement sera reconstitué instantanément par l'ordinateur à partir du tableau des matches et il n'est pas nécessaire de l'enregistrer.

Chaque tableau de résultats sera enregistré sous la forme d'un fichier texte. Les différentes cellules de la grille seront regroupées ligne par ligne en séparant les cellules par un caractère de tabulation (code 9). Il sera ainsi très facile, si on le veut, de relire ce fichier avec n'importe quel éditeur de texte et avec n'importe quel tableur.

Il faut commencer par ajouter dans la fiche, un composant SaveDialog (dans la palette Dialogues). On précisera la propriété DefautExt en y mettant txt.

Voici le texte du code associé à l'événement OnClick du bouton btEnregitrer :

procedure TfmChampionnat.btEnregistrerClick(Sender: TObject);
var i,j : integer;
    ch  : string;
    Fichier : TextFile;
begin
   SaveDialog1.InitialDir:=ExtractFileDir(ParamStr(0));
   //ParamStr(0) contient le nom et le chemin du fichier exécutable en cours
   //La procédure ExtractFileDir permet d'en extraire le chemin. Ainsi, sauf indi-
   //tion contraire, le fichier texte sera enregistré dans le dossier où se trouve
   //l'exécutable.

   if SaveDialog1.Execute then //Ouvre le dialogue d'enregistrement
   begin                       //on a appuyé sur le bouton Enregistrer
      AssignFile(Fichier,SaveDialog1.FileName);
      Rewrite(Fichier);        //On crée le fichier texte
      ch:=IntToStr(nbEquipes); //On écrit d'abord le nombre d'équipes
      writeln(Fichier,ch);
      for i:=0 to NbEquipes do //Pour chacune des lignes i de la grille
      begin
         ch:='';
         for j:=0 to NbEquipes do ch:=ch+grResultats.Cells[j,i]+chr(9);
         //La ligne i correspond aux cellules d'ordonnées i. Après chaque cellule,
         //on ajoute un caractère " tab " (chr(9))

         delete(ch,length(ch),1);//le dernier caractère " tab " est supprimé
         writeln(Fichier,ch);    //On écrit la ligne i dans le fichier
      end;
      CloseFile(Fichier);        //On ferme le fichier (Très important !)
      btEnregistrer.enabled:=false; //Le bouton Enregistrer devient inactif
   end;
end;

7. Ouvrir une grille de résultats déjà enregistrée

Il faut commencer par ajouter dans la fiche principale, un composant OpenDialog. On précisera, comme pour le composant SaveDialog1, que l'extension par défaut est 'txt'.

L'action sur le bouton btOuvrir est très complexe car le même bouton doit pouvoir servir à la fois pour ouvrir et pour fermer un fichier de résultats. Voici le code associé à un clic sur ce bouton :

procedure TfmChampionnat.btOuvrirClick(Sender: TObject);
{Attention le bouton btOuvir sert à la fois à fermer et à ouvrir une grille de résultats}
var reponse : word;
    i,j     : integer;
    ch      : string;
    Fichier : TextFile;
begin
   if btOuvrir.Caption='&Fermer' then //dans ce cas, le bouton sert à fermer
   begin
      if btEnregistrer.Enabled then //dans ce cas, la grille n'a pas été enregistrée
      begin
         reponse:=MessageDlg('Voulez-vous enregistrer avant de fermer ?',mtWarning,
                        [mbYes,mbNo,mbCancel],0);
         if reponse=mrYes then btEnregistrerClick(sender);
         if (reponse=mrNo)or(reponse=mrYes) then
         begin //dans les 2 cas on confirme la fermeture de la grille actuelle
            For i:=0 to grResultats.RowCount-1 do grResultats.rows[i].clear;
            grResultats.Visible:=false;
            btOuvrir.Caption:='&Ouvrir';
         end;
         if reponse=mrCancel then exit; //dans ce cas on renonce à fermer la grille
      end
      else //dans ce cas, la grille a été enregistrée et on la ferme
      begin
         For i:=0 to grResultats.RowCount-1 do grResultats.rows[i].clear;
         grResultats.Visible:=false;
         btOuvrir.Caption:='&Ouvrir';
      end;
   end
   else //dans ce cas, le bouton sert bien à ouvrir une grille enregistrée
   begin
      OpenDialog1.InitialDir:=ExtractFileDir(ParamStr(0));
      if OpenDialog1.Execute then
      begin
         AssignFile(Fichier,OpenDialog1.FileName);
         Reset(Fichier);
         Readln(Fichier,ch);
         nbEquipes:=StrToInt(ch);
         for i:=0 to NbEquipes do
         begin
            readln(Fichier,ch);
            j:=0;
            while(Pos(chr(9),ch))>0 do
            begin
               grResultats.cells[j,i]:=Copy(ch,1,Pos(chr(9),ch)-1);
               delete(ch,1,pos(chr(9),ch));
               inc(j);
            end;
            grResultats.cells[j,i]:=ch;
         end;
         CloseFile(Fichier);
         btEnregistrer.enabled:=false;
         grResultats.Visible:=true;
         grResultats.col:=1;
         grResultats.row:=2;
         grResultats.SetFocus;
         btOuvrir.Caption:='&Fermer';
         btClassement.Enabled:=true;
         btNouveau.Enabled:=false;
         btImprimer.Enabled:=true;
      end;
   end;
end;

<<TP précédent      TP suivant>>

Retour à la liste des TP