INFORMATIQUE
TP 12
Chronomètre analogique
Le but de ce TP est de réaliser un chronomètre, comme dans le TP précédent, mais cette fois en dessinant un vrai chronomètre à aiguilles. De plus, notre chronomètre sera plus précis car il sera réactualisé tous les dixièmes de seconde et non pas toutes les secondes.
Les boutons de contrôle sont les mêmes que dans le TP précédent. On pourra donc copier les composants du TP 11 et les recopier dans le nouveau projet :
Un bouton doit permettre d'initialiser la valeur de départ du chronomètre,
Un second bouton doit permettre de démarrer le chronomètre,
Un troisième bouton sert à arrêter le chronomètre : celui-ci reste figé jusqu'à ce qu'on appuie à nouveau sur le bouton de démarrage du chronomètre.
Enfin un dernier bouton sert à quitter l'application.
Il n'y a pas cette fois de composant Tpanel pour afficher le chronomètre car celui-ci sera dessiné directement sur le canvas de la fiche principale
Voici l'allure de la fiche principale :
Liste des composants et de leurs propriétés :
Fiche principale (TForm) Name fmChronometre Caption Chronomètre Bouton Initialisation (TButton) Name btInitialisation Caption &Initialisation Bouton Quitter (TButton) Name btQuitter Caption &Quitter Bouton Stop (TSpeedButton) Name btQuitter Glyph trffc14.bmp (représentant un panneau stop) Enabled False Bouton Chrono (TSpeedButton) Name btGo Glyph Timer01.bmp (représentant un chronomètre) Enabled False Enregistrer tout dans un nouveau dossier :
Unit1 sous le nom : uChronometre.pas Project1 sous le nom : Chronometre.dpr
3. Initialisation du chronomètre
Il faut commencer par ajouter au projet une nouvelle fiche :
Cette fiche est la même que celle du TP 11. On pourra donc recopier directement les composants (mais on peut également refaire entièrement la fiche : ce n'est pas très long).
Cette fiche, que l'on peut nommer fmInitialisation comporte :
Un composant TradioGroup :
Name = rgTypeChrono Items = " Normal ", " Compte à rebours " ItemIndex = 0 Un composant TgroupBox :
Name = gbValeurChronometre Caption = Valeur de départ du chronomètre
Ce composant contient lui-même 3 composants Edit , nommés edHeures, edMinutes et edSecondes associés à 3 composant UpDown, nommés udHeures, udMinutes et udSecondes. Composant UpDown 1 :
Name = udHeures Min = 0 Max = 23 Wrap = True (si on arrive à 23 on passe ensuite à 0) Composant UpDown 2 :
Name = udMinutes Min = 0 Max = 59 Wrap = True (si on arrive à 59 on passe ensuite à 0) Composant UpDown 3 :
Name = udSecondes Min = 0 Max = 59 Wrap = True (si on arrive à 59 on passe ensuite à 0)Pour faire apparaître cette fiche, il faut utiliser l'événement OnClick du bouton btInitialiser.
La procédure est très différente de celle du TP11 car il faut initialiser le dessin du chronomètre :
procedure TfmChronometre.btInitialisationClick(Sender: TObject);
var k,a,b,c,d,L : integer;
begin
with fmInitialisation do
if ShowModal=mrOK then
begin
Chrono:=(udSecondes.Position+udMinutes.Position*60+udHeures.Position*3600)*10;
btGo.Enabled:=true;
end;
with fmChronometre.Canvas do //initialisation du dessin du chronomètre
begin
pen.mode:=pmCopy;
x0:=width div 2;
y0:=height div 3;
R:= width div 4;
brush.color:=clSilver;
brush.style:=bsSolid;
pen.color:=clSilver;
rectangle(x0-R,y0-R,x0+R,y0+R); //Effacement du chronomètre précédent
brush.color:=clWhite;
pen.color:=clBlack;
Ellipse(x0-R,y0-R,x0+R,y0+R);
brush.Style:=bsClear;
Ellipse(x0-R+10,y0-R+10,x0+R-10,y0+R-10);
for k:=0 to 60 do
begin
if k mod 5 = 0 then L:=25 else L:=10;
a:=x0+round((R-10)*cos(pi/2-k*2*pi/60));
b:=y0+round((R-10)*sin(pi/2-k*2*pi/60));
c:=x0+round((R-10-L)*cos(pi/2-k*2*pi/60));
d:=y0+round((R-10-L)*sin(pi/2-k*2*pi/60));
MoveTo(a,b);LineTo(c,d);
end;
end;
end;La variable Chrono doit être déclarée globalement juste après la variable fmChronomètre. Elle contiendra en permanence, la valeur du chronomètre en dixièmes de secondes.
C'est évidemment la procédure clé. Elle est beaucoup plus compliquée que celle du TP 11. Elle suppose une bonne maîtrise non seulement du graphisme mais aussi de la trigonométrie. Vous trouverez ci-après le texte de cette procédure :
Procedure AfficherChronometre;
var Sec : integer;
a,b,c,d,L : integer;
begin
Sec:=Chrono mod 600; {Sec représente non pas les secondes mais les dixièmes de sec.}
with fmChronometre.Canvas do
begin
pen.mode:=pmNotXor;
L:=R div 2;
if Chrono mod 6000 =0 then //affichage des heures toutes les 10 mn
begin
a:=x0+round(L*cos(pi/2-(chrono/36000)*2*pi/12));
b:=y0-round(L*sin(pi/2-(chrono/36000)*2*pi/12));
pen.color:=clBlack; //aiguille des heures en noir
moveto(x0,y0);LineTo(a,b);
end;
if Sec mod 100 = 0 then //affichage des minutes toutes les 10 sec
begin
a:=x0+round(L*cos(pi/2-(chrono/600)*2*pi/60));
b:=y0-round(L*sin(pi/2-(chrono/600)*2*pi/60));
L:=(3*R) div 4;
c:=x0+round(L*cos(pi/2-(chrono/600)*2*pi/60));
d:=y0-round(L*sin(pi/2-(chrono/600)*2*pi/60));
pen.color:=clGreen; //aiguille des minutes en vert
moveto(a,b);LineTo(c,d);
end; //affichage des secondes à chaque dixième de seconde
a:=x0+round(R*cos(pi/2-Sec*2*pi/600));
b:=y0-round(R*sin(pi/2-Sec*2*pi/600));
c:=x0+round((R-10)*cos(pi/2-Sec*2*pi/600));
d:=y0-round((R-10)*sin(pi/2-Sec*2*pi/600));
pen.color:=clRed; //aiguille des secondes en rouge
moveto(a,b);LineTo(c,d);
end;
end;
La encore, la technique employée n'est pas du tout évidente car elle fait appel à une fonction obscure de l'API de Windows : GetTickCount. Cette fonction indique le nombre de millisecondes écoulées depuis le démarrage de Windows. Contrairement au TP11, le déclenchement de l'affichage du chronomètre se fera tous les dixièmes de seconde (donc tous les 100 millièmes de secondes) :
procedure TfmChronometre.btGoClick(Sender: TObject);
var Top : DWord;
begin
AfficherChronometre;
btStop.Enabled:=true;
btGo.Enabled:=false;
Stop:=false;
Top:=GetTickCount;
repeat
Application.ProcessMessages;
if GetTickCount-Top>100 then //100 = 1 dixième de seconde
begin
if fmInitialisation.rgTypeChronometre.ItemIndex=0 then inc(Chrono)
else dec(Chrono);
Top:=GetTickCount;
AfficherChronometre;
end;
until stop;
end;La variable Stop, de type Boolean, doit être déclarée juste après la variable Chrono.
Le type Dword, désigné pour la variable Top est un type entier sur 4 octets. Il est nécessaire car la fonction GetTickCount retourne un entier de ce type.
La ligne Application.ProcessMessages; est absolument indispensable car elle dit au programme de surveiller les événements qui pourraient survenir pendant les calculs. Si on ne fait pas cela, alors un clic sur le bouton Stop sera sans effet !Voici, enfin, la procédure correspondant au clic sur le bouton Stop :
procedure TfmChronometre.btStopClick(Sender: TObject);
begin
Stop:=true;
btGo.Enabled:=true;
btStop.Enabled:=false;
end;
Voici l'état du chronomètre au bout de 2 minutes, 20 secondes et quelques dixièmes :
Bien entendu, il est tout à fait possible d'améliorer le graphisme ... mais c'est à vous de jouer !