2 - Création d'un composant graphique.
Dans cet exemple nous allons créer un composant horloge de type analogique.
La procédure de création est la même que dans la première partie, nous ne reviendrons donc pas dessus.
La seule différence est que notre composant descend de TGraphicControl. TGraphicControl est
l'ancêtre de nombreux contrôles graphiques légers, n'ayant pas besoin des entrées clavier
ou de contenir d'autre contôles. Il possède une propriété Canvas pour dessiner
et une méthode virtuelle Paint pour l'actualisation les dessins.
TLabel est un descendant de TGraphicControl.
Code :
Fichier "horloge.h"
#ifndef HorlogeH
#define HorlogeH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Controls.hpp>
#include <Classes.hpp>
//---------------------------------------------------------------------------
class PACKAGE THorloge : public TGraphicControl
{
private:
TTimer *Timer1;
Graphics::TBitmap *Image1;
bool FSec;
bool __fastcall CanResize(int &NewWidth, int &NewHeight);
protected:
void __fastcall Tempo(TObject* Owner);
void __fastcall Paint();
public:
__fastcall THorloge(TComponent* Owner);
virtual __fastcall ~THorloge();
__published:
__property Anchors;
__property ParentShowHint;
__property ShowHint;
__property Visible;
__property bool Seconde = {read=FSec, write=FSec, default=true};
};
//---------------------------------------------------------------------------
#endif
Dans le fichier en-tête "horloge.h" partie private nous déclarons un pointeur Timer1 sur un TTimer
qui donnera le tempo de rafraîchissement du dessin,
un pointeur sur un TBitmap qui nous servira de support pour dessiner, une donnée membre booléenne
FSec qui mémorisera la propriété Seconde pour l'affichage de l'aiguille des secondes, une
méthode Tempo pour actualiser le dessin des aiguilles, puis les méthodes
CanResize et Paint en vue de les redéfinir.
Dans la partie public nous déclarons le destructeur de notre composant.
Dans la partie __published nous déclarons les propriétés que l'on veut rendre visible
et la nouvelle propriété Seconde qui nous permettra d'afficher ou ne pas afficher
l'aiguille des secondes.
Fichier "horloge.cpp"
#include <vcl.h>
#include <math.h> //rajouté manuellement pour sin, cos.
#pragma hdrstop
#include "Horloge.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck est utilisé pour vérifier que les composants créés n'ont
// aucune fonction virtuelle pure
//
static inline void ValidCtrCheck(THorloge *)
{
new THorloge(NULL);
}
//---------------------------------------------------------------------------
__fastcall THorloge::THorloge(TComponent* Owner)
: TGraphicControl(Owner)
{
ControlStyle << csOpaque;
Image1 = new Graphics::TBitmap();
Timer1 = new TTimer(NULL);
Timer1->OnTimer = Tempo;
FSec=true;
Width = 120;
Height = 120;
Image1->Width = Width;
Image1->Height = Height;
}
//---------------------------------------------------------------------------
__fastcall THorloge::~THorloge()
{
delete Image1;
delete Timer1;
}
//---------------------------------------------------------------------------
// Pour ne pas changer les dimensions du composant.
bool __fastcall THorloge::CanResize(int &NewWidth, int &NewHeight)
{
NewWidth = 120;
NewHeight = 120;
return true;
}
//---------------------------------------------------------------------------
// Rafraichissement du dessin selon le tempo.
void __fastcall THorloge::Tempo(TObject* Sender)
{
Invalidate();
}
//---------------------------------------------------------------------------
//Dessin du composant
void __fastcall THorloge::Paint()
{
float n,z,u,x0,y0,x1,y1,x2,y2,x3,y3;
TDateTime heure;
TPoint points[4];
Word wHour, wMinute, wSeconde;
heure = Now();
DecodeTime(heure, wHour, wMinute, wSeconde, NULL);
// Couleur de fond.
Image1->Canvas->Brush->Color = Color;
Image1->Canvas->Pen->Color = Color;
Image1->Canvas->Rectangle(0,0,Image1->Width,Image1->Height);
// Dessin du cercle.
Image1->Canvas->Pen->Color = clBlack;
Image1->Canvas->Pen->Width = 2;
Image1->Canvas->Ellipse(2,2,118,118);
// Dessin du texte.
Image1->Canvas->Font->Name = "Arial";
Image1->Canvas->Font->Color = clBlack;
Image1->Canvas->Font->Size = 8;
Image1->Canvas->TextOut( 50,80,"CGi");
Image1->Canvas->TextOut( 55,6,"12");
Image1->Canvas->TextOut( 58,101,"6");
Image1->Canvas->TextOut( 8,53,"9");
Image1->Canvas->TextOut( 105,53,"3");
Image1->Canvas->TextOut( 81,13,"1");
Image1->Canvas->TextOut( 97,30,"2");
Image1->Canvas->TextOut( 98,76,"4");
Image1->Canvas->TextOut( 81,94,"5");
Image1->Canvas->TextOut( 33,94,"7");
Image1->Canvas->TextOut( 15,76,"8");
Image1->Canvas->TextOut( 14,30,"10");
Image1->Canvas->TextOut( 32,13,"11");
Image1->Canvas->Pen->Width = 1;
// Dessin aiguille des secondes si FSec à true.
if (FSec)
{
n = wSeconde*200/60;
z = n/100*3.14159;
u = (n+50)/100*3.14159;
x0 = sin(z)*50;
y0 = -cos(z)*50;
x1 = -sin(z)*10;
y1 = cos(z)*10;
x2 = sin(u)*2;
y2 = -cos(u)*2;
x3 = -sin(u)*2;
y3 = cos(u)*2;
points[0] = Point(x1+60,y1+60);
points[1] = Point(x2+60,y2+60);
points[2] = Point(x0+60,y0+60);
points[3] = Point(x3+60,y3+60);
Image1->Canvas->Brush->Color = clRed;
Image1->Canvas->Polygon(points, 3);
}
// Dessin aiguille des minutes.
n = wMinute*200/60 + wSeconde*200/60/60;
z = n/100*3.14159;
u = (n+50)/100*3.14159;
x0 = sin(z)*50;
y0 = -cos(z)*50;
x1 = -sin(z)*10;
y1 = cos(z)*10;
x2 = sin(u)*4;
y2 = -cos(u)*4;
x3 = -sin(u)*4;
y3 = cos(u)*4;
points[0] = Point(x1+60,y1+60);
points[1] = Point(x2+60,y2+60);
points[2] = Point(x0+60,y0+60);
points[3] = Point(x3+60,y3+60);
Image1->Canvas->Brush->Color = clAqua;
Image1->Canvas->Polygon(points, 3);
// Dessin aiguille des heures.
n = wHour*200/12 + wMinute*200/60/12;
z = n/100*3.14159;
u = (n+50)/100*3.14159;
x0 = sin(z)*35;
y0 = -cos(z)*35;
x1 = -sin(z)*10;
y1 = cos(z)*10;
x2 = sin(u)*4;
y2 = -cos(u)*4;
x3 = -sin(u)*4;
y3 = cos(u)*4;
points[0] = Point(x1+60,y1+60);
points[1] = Point(x2+60,y2+60);
points[2] = Point(x0+60,y0+60);
points[3] = Point(x3+60,y3+60);
Image1->Canvas->Brush->Color = clYellow;
Image1->Canvas->Polygon(points, 3);
Canvas->Draw(0,0,Image1);
}
//---------------------------------------------------------------------------
namespace Horloge
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(THorloge)};
RegisterComponents("MesCompo", classes, 0);
}
}
Passons maintenant en revue l'implémentation de notre composant, fichier horloge.cpp:
Dans le constructeur: Nous ajoutons à la propriété ControlStyle
de notre composant l'attribut csOpaque, cela évite de petits scintillements résiduels.
Nous créons le Bitmap (Image1). Nous créons le Timer Timer1 et nous assignons la méthode
Tempo à l'événement OnTimer de Timer1, la propriété Interval du Timer étant à une seconde par défaut
nous ne la changeons pas, cela correspond à l'intervalle de rafraîchissement de notre horloge.
La donnée membre FSec est initialisée à true, l'aiguille des secondes étant visible par défaut.
Puis nous définissons les dimensions du composant et du Bitmap.
Dans la définition de la méthode Tempo nous allons appeler la méthode Invalidate
ce qui va rafraîchir le dessin notre horloge toutes les secondes.
Tempo est appelée par l'événement OnTimer de Timer1.
La méthode CanResize retourne par référence toujours les mêmes dimensions, car
notre composant a des dimensions fixes que l'on ne pourra changer.
La partie dessin à proprement parler se trouve dans la méthode Paint.
Cette méthode est appelée à chaque fois que notre composant doit être redessiné, soit
parce qu'il a été caché par une autre fenêtre, soit toutes les secondes pour afficher
la nouvelle position des aiguilles.
Dans un composant graphique on dessine toujours sur la méthode Paint. Elle est appelée
par un message (WM_PAINT) envoyé par Windows quand cela est nécessaire.
Pour éviter l'effet de scintillement nous dessinerons sur un bitmap (TBitmap) auquel
nous avons accès par le pointeur Image1
et qui sera restitué à l'écran une fois le dessin terminé par la méthode Draw
du canvas de notre composant.
En ce qui concerne le dessin sur le Bitmap Image1 nous dessinons de façon classique
à l'aide de son Canvas donc sans commentaire. Les aiguilles sont dessinées avec la méthode Polygon
dont les coordonnées sont calculées à l'aide des fonctions trigonométriques sin et cos.
La méthode Polygon a comme arguments un pointeur sur un tableau de TPoint qui contient les coordonnées
de chaque point du polygone et un int qui contient l'indice du dernier élément du tableau de TPoint.
Dans le destructeur de notre composant nous détruirons les objets Image1 et Timer1.
Ce composant a été compilé et testé sur BCB4 et BCB6.
CGi
Avec la contribution d'Alacazam pour la relecture.
Télécharger les sources.
(Est inclus le fichier "horloge.dcr" contenant l'icône qui le représente
sur la palette de composants)
|