cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 2018
-
10 janv. 2007 à 13:04
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 2018
-
14 janv. 2007 à 09:40
Bonjour,
Je créé un composant dérivant de TgraphicControl qui dessine différentes formes dont je donne un extrait de l'une d'elle, plus bas. J'ai rendu publique une procédure paint qui fait appel à cette fonction, mais je ne suis pas sûr que ce soit la bonne méthode.
Je souhaiterai supprimer la référence à Form1, mais je n'ai pas trouvé de solution.
Dois-je créer un Handle de Canvas, mais comment dire ensuite au composant que le parent de TLed peut être quelconque. Dans la procedure de création de la fiche principale, je déclare bien aLed.Parent := Form1; , mais cela ne semble pas indiquer à la procedure Paint du composant que le Canvas de la Form1 est l'endroit où il faut dessiner!!!???!!
procedure TLed.DrawLedCircle(Centre:Tpoint;aDex,aDInt:Byte;ClEx,ClIn:TColor);
var R : Byte;
begin
With Form1.Canvas do
begin
Brush.Color := ClEx;
Pen.Color := ClEx;
Pen.Width := 1;
R := aDEx div 2;
Ellipse(Centre.X-R,Centre.Y-R,Centre.X+R,Centre.Y+R);
...
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
aLed := TLed.create(Self); // création d'1 led de type TLed
aLed.Parent := Form1;
...
end;
Voilà, je m'en remet à vos lumières delphistes.
Merci à vous!
procedure TLed.Paint;
begin
with Canvas do
begin
Pen := FPen;
Brush := FBrush;
Case FTypeLed of sstCircle : DrawLedCircle(Canvas,Centre,DiamExt,DiamInt,ColorExt,ColorInt);
...
end;
...
end;
end;
La référence au Canvas est accessible partout dans les méthodes de TLed, c'est le principe même des objets !
Tu peux taper Self.Canvas si tu veux, cela revient au même.
D'ailleurs, tu n'as nullement besoin de mettre Led.Paint dans le OnPaint de ta fiche, tout est géré nativement. De plus, je ne te préconise pas l'appel à Invalidate dans le constructeur du composant. suivant les cas, tu peux ariver à un plantage (ressources pas encore allouées ou autres erreurs...). De toutes façons les composants sont automatiquement dessinés lorsque c'est nécéssaire, tu n'as donc pas trop à t'en faire, juste un Refresh lors des changements des propriétés.
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 12 janv. 2007 à 17:49
Merci Guillemouze et Japee de votre participation. j'ai tardé à répondre car je voulais faire un résumé des nombreuses modifications du code en cours d'élaboration.
Un grand merci à Cirec et Florenth qui ont relu mon code et procéder à des corrections.<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" /??>
L'erreur venait essentiellement de paramètres par défaut mal initialisés. Donc une vérification des bornes du composant ont été ajoutées.
Je résume quelques conseils que Cirec et Florenth m’ont donné dans l’écriture du composant. Je les restitue ici aussi fidèlement que possible :
1-
Suppression des surchages de méthodes natives utilisant Pen et Brush . En effet, le choix de TgraphicControl avait été fait parce qu’il possède déjà Canevas qui possède lui-même les entités Pen et Brush.
J’avais dans l’idée au départ d’utiliser les régions qui me semblent plus performantes pour la composition de dessins. Mais je pense que c’est une erreur car me semble t-il, les régions sont surtout utilisées lorsqu’il n’y a pas de canevas de disponible. Est-ce que je dis des conneries ?
2- Dans les méthodes on utilise toujours les variables Private celles qui commencent
par la lettre F Ex: FCentre au lieu de Centre
3-
Bien initialiser toutes les variables dans le constructeur et ne jamais faire appel à Invalidate dans le constructeur.
4-
La méthode Paint doit toujours être placée dans la partie protected.
5-
J’ai supprimé tous les passages de valeurs aux variables des méthodes de dessin car ces variables sont définies dans l’objet.
La procédure DrawLedEllipse(Centre:Tpoint;aDex,aDInt:Byte;ClEx,ClIn:TColor); devient simplement procedure DrawLedEllipse ;
6- La Procedure Register ne nuit pas au fonctionnement en Objet si on n’a pas installé le composant.
7-
Published est réservé pour les propriétés ; aucune procédure ne doit y apparaître.
8-
Les règles d’écritures en Delphi : Lorsqu'on déclare des types énumérés, les premières lettres de chaque constante à l'intérieur sont les initiales en minuscule du nom du type. On écrira donc: TLedType = (tlRectangle, tlSquare…);
Guillemouze
Messages postés991Date d'inscriptionsamedi 25 octobre 2003StatutMembreDernière intervention29 août 20136 12 janv. 2007 à 18:27
heuuuu je ne suis pas d'accord sur le point 8, j'aurai plutot ecrit TLedType = ( lt Rectangle, ltSquare)
tout comme TMonEnum = (meElement1, meElement2)
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 12 janv. 2007 à 18:28
Oui, c'est tout à fait ça Jean_Jean, bonne synthèse d'ailleurs, pratique pour ceux qui passeront par là.
A une seule chose près: les régions ne sont pas vraiment plus performantes que les fonction GDI correspondantes. Tout dépend l'effet voulu. Si tu veux un composant rond (comme une led tiens ... ^^) tu es obligé d'utiliser les régions pour lui donner sa forme, mais ensuite, tu dessines dans le canvas avec les fonctions GDI.
Attention, de plus, pour pouvoir utiliser les régions, il faut que ton composant dérive de TWinControl ou de ces descendants (qu'il aît un Handle en fait) et lui donner sa forme grâce à la procédure SetWindowRgn() lors de la création de la fenêtre ou de sa modification. Ce qui fait somme toute un truc assez compliqué pour pas grand chose. Surtout que les TWinControl consomment plus de mémoire (tout comme les régions).
Attention aussi à ce conseil de Cirec
"
2
-
Dans les méthodes on utilise toujours les variables Private celles qui commencent
par la lettre F Ex: FCentre au lieu de Centre"
Dans certains cas, il est impératif ou alors plus pratique d'utiliser la propriété (publique ou publiée), lorsque celle ci a un Getter ou un Setter (ou alors appeler directement la bonne méthode). Maisce n'est pas un cas très fréquant. Je le dis juste pour l'info.
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 13 janv. 2007 à 15:44
Salut,
juste un petit truc au passage :
Florenth à dit : "Attention, de plus, pour pouvoir utiliser les régions, il faut que ton composant dérive de TWinControl...
"
Ce n'est pas obligatoire ... on peut appliquer une région à un Canvas
Ensuite l'appel à une propriété Publiée peut avoir de fâcheuses conséquences ... (boucle sans fin pour peut que l'on fasse appel a elle dans le Setter ou Getter ... Voir plus loin )
Et de plus faire appel à une propriété publiée dans un composant ou objet provoque un double appel :
Private FCenter : TPoint;
Procedure SetCenter(Value : TPoint);
Function GetCenter: TPoint;
Published Center : TPoint Read GetCenter Write SetCenter;
End;
Implementation
Procedure SetCenter(Value : TPoint);
Begin If Value <> FCenter Then Begin FCenter := Value;
Invalidate ;
End;
End;
Function GetCenter: TPoint;
Begin Result : = FCenter;
End;
Donc ... un appel à FCenter ne fait appel qu'a FCenter
Par contre utiliser directement Center fera en premier appel au Setter ou Getter donc à FCenter ... résultat deux appel pour une seul et même valeur ...
Exemple type de ce qu'il ne faut pas faire sous peine de déclancher une boucle sans fin Procedure SetCenter(Value : TPoint);
Begin
If Value <> FCenter Then Begin
Center := Value;
Invalidate ;
End;
End;
Le seul moment ou un appel à la propriété Publique est préférable c'est si une opération quelconque est faite en plus dans le Setter ... Mais en général c'est un défaut de conception (enfin c'est mon avis)
ps : Si votre Setter est Irlandais ... je vous conseil une vaccination anti-rabique d'urgence
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 10 janv. 2007 à 13:36
Merci Japee pour ta réponse!
Oui, dans le code que tu avais déposé, j'ai vu cette référence .Create(AOwner: TComponent); souvent utilisée. Je l'ai Remplacé par aLed :
Constructor Tled.Create (aLed : TComponent);
begin
inherited Create(aLed);
FPen := TPen.Create;
...
end;
Bon, je vais étudié cette piste...
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 10 janv. 2007 à 14:47
Bonjour Cirec,
j'ai fait la rectif de AOwner dans le constructor et les différents tests de vos propositions de With, ça ne dessine pas!
j'ai fait remonter la référence Form1.Canvasdans Paint plûtot que Draw...,mais ça ne change pas le problème. il veut une référence Canvas Valide. je récapitule le code:
fiche principale :
procedure TForm1.FormCreate(Sender: TObject);
begin
aLed := TLed.create(Self); // création d'1 led de type TLed
aLed.Parent := Form1;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
aLed.Paint;
end;
Unité composant:
Constructor Tled.Create (AOwner : TComponent);
begin
inherited Create(AOwner);
...
Invalidate;
end;
procedure TLed.Paint;
var aCanv : Tcanvas;
begin
aCanv := Canvas as (aOwner as TComponent));// invalide ne connait pas aOwner et Erreur opérateur non autorisé...
with aCanv do
begin
Pen := FPen;
Brush := FBrush;
Case FTypeLed of
sstCircle : DrawLedCircle(aCanv,Centre,DiamExt,DiamInt,ColorExt,ColorInt);// j'ai mis aCanv pour supprimer with Canvas ds la procédure ...
end;
...
end;
end;
TLed dérive directement de TGraphicControl pour disposer du Canvas. Mais Je n'arrive pas intégrer AOwner ou Canvas dans le with de TLed.Paint.
Je préférerai une référence de handle quelconque plutot que TForm uniquement avec With Canvas as Owner as TForm...
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 10 janv. 2007 à 15:43
Merci Florenth et Cirec pour vos conseils!
D'ailleurs, c'est sur 1 idée de Florenth que je me lance dans l'aventure des composants! Vilain qu'il est de me donner du fil à retordre. Mais tu as raison, l'écriture de composant, c'est quand même sympa, même si je n'ai pas beaucoup de temps, je me détends en faisant du delphi. En plus j'ai retrouver mon D5. alors, je peux y balancer des codes à tester!
Je compulse la doc, mais c'est un univers!...
Dans la doc, j'avais lu que invalidate forçait l'évènement onpaint ou un refresh! Mais si tu dis que ça pose pb!?!, je supprime.
ec
Je veux bien vous balancer l'unité complète, mais elle n'est pas très avancée et enconbrante pour un post.
Je reprendrai contact ce soir, je dois m'absenter!
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 10 janv. 2007 à 20:49
Bon ça fait une heure que j'essaye de comprendre l'erreur! NO comprendo!
En regardant des exemples de compo, je ne vois pas grande différence avec mon code. Sauf pour paint qui se trouve en général dans la partie protected et que j'ai mis en publique pour lancer le dessin. D'ailleurs, en replaçant Paint dans la zone protected, même en remettant Form1.Canvas dans la procédure de dessin, ça ne dessine plus, ce qui est logique.
Mais comme l'a dit Cirec, normalement, avec la création dans la fiche principale, l'affichage est automatique.
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 13 janv. 2007 à 09:58
Merci à tous.
Pour les Régions, c'est le fait de changer d'ancêtre qui m'a freiné.
Par contre je me posai la question effectivement de la précision du dessin et j'envisage d'augmenter la résolution sur le canvas de Graphiccontrol
Je ne sais si c'est possible sur ce composant, alors que pour les régions, je sais que c'est possible.
cs_Jean_Jean
Messages postés615Date d'inscriptiondimanche 13 août 2006StatutMembreDernière intervention13 décembre 20183 13 janv. 2007 à 16:14
Merci Cirec pour ces précisions.
Je vais introduire les fonctions Getxxx qui gagnent en lisibilité.
Bonne nouvelle pour les régions. Je vais essayer sans d'abord. Je pense utiliser PixelFormat := pf24bit pour introduire une meilleure résolution.
Je suis toujoursà réfléchir sur les fonctionnalités à écrire pour en faire un outil sympha.
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 13 janv. 2007 à 16:17
Et juste un autre petit truc au passage:
Si ton Setter ressemble à ça :
procedure TMyClass.SetObject(Value: TObject)
begin
if Value <> FObject then
begin
FObject.Free;
FObject := Value;
if Assigned(FObject) then FObject.OnXXxxxx : = doSth;
end;
end;
L'affectation directe de la propriété permet d'éviter le FObject.OnXXxx := doSth à chaque fois qu'on change sa valeur. Sinon, c'est vrai que les Getter/Setters sont à utiliser avec parcimonie mais dnas certains cas, ils sont bien pratiques.
"si une opération quelconque est faite en plus dans le Setter ... Mais en général c'est un défaut de conception (enfin c'est mon avis) "
> Pas convaincu. Regarde TStringList.SetCapacity, ils font bien des choses en plus. Est-pour autant mal conçu, je n'en suis pas sûr...
"boucle sans fin pour peut que l'on fasse appel a elle dans le Setter ou Getter"
> Il faut vraiment le faire exprès pour ne pas s'en rendre compte...
"Par contre utiliser directement Center fera en premier appel au Setter
ou Getter donc à FCenter ... résultat deux appel pour une seul et même
valeur ... "
> Tout à fait d'accord, c'est du temps de perdu pour rien ! Sauf pour les Getters de tableau où c'est la seule solution pour avoir un accès correct sans ce casser la tête. Tu peux très bien faire GetItem(I).xxx ce qui t'évites de faire (FItems[I] as Txxx).xxx dans la plupart des cas.
"on peut appliquer une région à un Canvas"
> Tu peux utiliser FillRgn() et les autres avec un Handle de Canvas. Par contre, tu ne peux pas donner une forme ronde avec SetWindowRgn(). Il te faut impérativement un Handle de fenêtre.