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 :
- Saisir les noms des équipes (en début de championnat),
- Définir le nombre de points obtenus en cas de match gagné, de match perdu, de match nul,
- Utiliser un second critère pour le classement des ex æquo (par exemple le " goal average "),
- Saisir les résultats de tous les matches au fur et à mesure qu'ils arrivent,
- Enregistrer sur disque le tableau des résultats,
- Afficher le classement du championnat à tout moment pendant le championnat,
- Imprimer le tableau des résultats,
- Imprimer le tableau du classement.
2. Mise en place de la fiche principale
La fiche principale doit ressembler à ceci :
Les propriétés de la fiche sont les suivantes :
- Name : fmChampionnat
- Caption : Championnat
Placer sur la fiche un composant StringGrid qui doit occuper la majeure partie de la fiche. Ses propriétés sont les suivantes :
- Name : grResultats
- Visible : false
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 :
- Pour l'Unit1, créer un sous dossier dans votre dossier, nommé Championnat et enregistrer Unit1 dans ce sous-dossier sous le nom uChampionnat.
- Pour le Project1, enregistrer également dans le sous dossier qui vient d'être créé, sous le nom Championnat.
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é.
- On commence par mettre un composant GroupBox qui servira de cadre pour définir les différents paramètres :
Name : gbParametres
Caption : Paramètres du classement
- On place ensuite 4 composants Label dont les propriétés Caption permettent d'afficher les 4 textes.
- On place ensuite 3 composants Edit en réduisant leur largeur et en effaçant leur texte (propriétés Text vide pour le moment). On les nomme respectivement :
edMatchGagne
edMatchPerdu
edMatchNul.
Il faut les placer bien en face des composants Label, et pour cela, il faut généralement modifier leur propriété Top.
- On met ensuite 3 composants UpDown
(dans la palette de composants Win32). Pour qu'ils soient bien collés aux composants Edit, il faut utiliser leur propriété Associate. Voici le détail des propriétés (en respectant l'ordre de haut en bas):
Name : udMatchGagne ; udMatchPerdu ; udMatchNul
Associate : edMatchGagne ; edMatchPerdu ; edMatchNul
Position : 3 ; 0 ; 1
Max : 10 ; 10 ; 10
Min : 0 ; 0 ; 0
On met enfin le composant RadioGroup, toujours à l'intérieur du composant gbParametres (on sélectionne le cadre gbParametres, puis on choisit le composant RadioGroup dans la palette Standard et on précise les propriétés :
Name : rgClassementExAequo
Caption : Classement des ex aequo
Items : cette propriété permet d'ouvrir un éditeur de listes de chaînes. On y mettra 3 lignes :
Buts marqués
Buts concédés
Différence de buts
ItemIndex : 2L'utilisateur pourra ainsi choisir la manière dont les ex æquo seront départagés. Dans le premier cas, en cas d'ex æquo c'est l'équipe qui aura marqué le plus de buts qui l'emportera, dans le second cas, c'est l'équipe qui aura concédé le moins de buts et dans le dernier cas, c'est celle qui aura la meilleure diférence entre le nombre de buts marqués et le nombre de buts concédés.
- Enregistrer tout, en donnant à Unit1 le nom uNouveau (chaque fiche ajoutée dans un projet, entraîne la création d'une nouvelle unité).
- Cliquer sur la fiche " Nouveau championnat " (en dehors de l'un de ses composants) et, dans l'inspecteur d'objets, onglet Evénements, choisir l'événement OnActivate puis compléter le code comme ci-dessous :
procedure TfmNouveau.FormActivate(Sender: TObject);
var i : integer;
begin
with grNomsDesEquipes do
begin
cols[1].clear;
Cells[0,0]:='numéro';
Cells[1,0]:='nom de l''équipe';
ColWidths[0]:=60;
ColWidths[1]:=width-85;
for i:=1 to 20 do cells[0,i]:=inttostr(i);
end;
end;
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 :
- Enregistrer tout.
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;