INFORMATIQUE

TP 12

Chronomètre analogique


1. Objectif

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

2. Mise en place des principaux composants.

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 :

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 :

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.

4. Affichage du chronomètre

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;

5. Démarrage du chronomètre

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 !

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

Retour à la liste des TP