Bonjour à tous,
Non je ne programme pas sur la plage mais au milieu de mes cartons! Mais si comme je le pense vous avez emmené votre pc sur la plage, vous pourrez peut-être éclaircir un point.
Je suis en train de définir un composant graphique de B-arbre. Le choix des étiquettes des n'uds peut être de différentes formes avec différentes couleurs etc.
Voici l'une des procédures de dessin d'une étiquette de forme circulaire:
{-----------------------------------------
procedure TTreeShape.DrawShadowEtiqCircle;
var rl,rt,rw,rh: Integer;
begin
if Shadow then
begin
{ Calcul dimensions width et height de l'ombre}
...
Canvas.Ellipse(rl,rt,rl+rw,rt+rh);
end;
end;
procedure TTreeShape.DrawEtiqCircle;
var rl,rt,rw,rh: Integer;
begin
{ Calcul dimensions width et height de l'étiquette}
...
Canvas.Ellipse(rl,rt,rl+rw,rt+rh);
end;
J'ai donc écrit différentes fonctions, mais je ne sais pas trop ou je dois écrire la fonction d'effacement de l'ancienne forme des étiquettes en cas de changement de forme. La fonction de dessin ci-dessus est appelée par :
procedure TTreeShape.PaintTree;
begin
With Canvas do
begin
Canvas.Brush.Color := FBrush.Color;
Canvas.Pen.Color := FPen.Color;
Case FEtiquette of
teCircle : DrawEtiqCircle;
teEllipse : DrawEtiqEllipse;
?
end;
end;
end;
J'ai défini également les procédures:
procedure TTreeShape.ChangeRedraw(Sender: TObject);
begin
Invalidate;
end;
procedure TTreeShape.SetEtiquette(value: TTypeEtiquet);
begin
if value <> FEtiquette then
begin
FEtiquette := value;
Paint;
end;
end;
Je ne sais plus par quel bout réfléchir.
1. A votre avis que dois-je faire pour effacer l'ancienne image? Dans qu'elle procédure dois-je écrire le code?
2. L'image de l'arbre (liens, étiquettes, données) seront dessinés dans un bitmap.
Ca aurait rempli le rectangle de l'étiquette de blanc, te laissant libre de dessiner une forme quelconque par-dessus sans imperfections. On peut obtenir le même résultat avec une couleur de fond différent et jouant avec Canvas.Rectangle et Pen. J'aurai mis ça avant tous les Appels à Ellipse. Peux-tu préciser ton problème un peu plus ?
1°) il serait intéressant de nous dire de quel composant tu dérive ton "TTreeShape" ?
puisqu'en fonction du composant ancêtre choisi tu peux surcharger la méthode "Paint" qui est prévue pour ça.
donc dans ce cas l'effacement interviendrait au début de cette méthode et suivrait le code de "PaintTree" ce qui ressemblerait un peu à ça:
with inherited Canvas do
begin
Brush.Color := FBrush.Color;
Pen.Color := FPen.Color;
// <--- ICI on efface
FillRect(ClipRect);
case FEtiquette of
teCircle : DrawEtiqCircle;
teEllipse : DrawEtiqEllipse;
end;
end;
et tpour finir tu peux alors "Invalidate" partout à la place de "Paint" comme dans "SetEtiquette"
Invalidate dit à Windows que la totalité de la représentation graphique du composant a été changée, et qu'il faut mettre à jour le plus tôt possible. Windows envoie alors un message WM_PAINT au composant qui le traite et appelle Paint en conséquence. En appellant Paint, tu forces un peu la main
[quote=Aide Delphi]Description
Utilisez Invalidate quand la totalité du contrôle doit être dessinée. Si plusieurs zones d'un contrôle doivent être redessinées, Invalidate provoque le réaffichage de toute la fenêtre en une seule passe, pour éviter les instabilités provoquées par les redessins redondants. Les performances ne sont pas dégradées par plusieurs appels d'Invalidate avant que le contrôle ne soit effectivement redessiné.
il me semble que l' on peut "invalider" une partie d' un compo avec InvalidateRect.
Par exemple, le TDBGrid utilise cela pour éviter de repeindre toute la grille lorsque l' on passe par exemple d' un colonne à l' autre: on ne met à jour que les cellules concernnées (sauf si on provoque un scroll horizontal lors du changement de colonnes).
Pour ma part, j' ai opté pratiquement tout le temps pour le Invalidate dans mes compos, qui sont bien entendu plus simple que le TDBGrid (à l' exeption de mes DBGrids héritant du TDBGrid et possède donc cette fonctionnalité "héritée").
La seule exception, c' est mon TcyColorMatrix qui permet de mettre à jour (graphiqument donc ...) que partiellement un bitmap utilisé pour sauvegarder tout le graphique du compo. Dans le OnPaint, je fais juste canvas.DrawBitmap(monBitmap).
C' est à dire que lorsque je change une zone de mon compo, je mets à jour cette zone dans le Bitmap puis j' appelle Invalidate (qui va donc appeler l' événement Paint), on pourrait aussi peindre directement sur le canvas simplement la zone modifiée.
En fait, il y a 2 critères à prendre en compte:
- la lenteur à redessinner le compo: teste combien de TonCompo.Update tu arrives à faire par seconde pour voir s' il est necessaire d' optimiser ton compo (utilise la fonction GetTickCount).
- déterminer avec quelle fréquence le compo est susceptible de faire un paint: le cas de la DBGrid est un bon exemple: si j' appuis sur la toucha clavier "bas", je vais parcourir tous les enregistremnts jusqu' à la fin du fichier. Il faut donc que la mise à jour soit rapide pour une bonne fluidité du compo.
oup
une petite erreur c'est glissée dans l'avant dernière ligne ... il fallait lire :
et pour finir tu peux alors appeler "Invalidate" partout à la place de "Paint" dans "SetEtiquette" par exemple.
Merci pour vos réponses... Le soleil ne vous tape pas sur les neurones, quel bonheur!!!
Mon composant dérive de :
TTreeShape = class(TGraphicControl)
private
Rect : TRect;
MemDC : HDC;
Bitmap : HBitmap;
FBrush : TBrush;
FPen : TPen;
FShadow : Boolean;
FShadowOffset : Cardinal;
FShadowColor : TColor;
FEtiquette : TTypeEtiquet;
...
@Cirec, oui je pense à priori comme toi, j'ai surchargé paint pour définir la mémoire dont j'ai besoin et dessiner les différents éléments du graphique...D'ailleurs, c'est en construction car je vais devoir dessiner les étiquettes, les ombres, les liens et les données... => dessin dans un bitmap global, c'est plus logique.
voici ma méthode paint (non définitive et en test) :
procedure TTreeShape.Paint;
var
rc: bool;
oldBmp: HBitmap;
oldThumb: TRect;
begin
inherited Paint;
with Canvas do
begin
Brush.Color := FBrush.Color;
// FillRect(Canvas.ClipRect);
// Pen.Mode := pmblack;//pmxor;
{Création mémoire pour lasauvegarde du bitmap}
if MemDC = 0 then MemDC := CreateCompatibleDC(Canvas.Handle);
{Create image arbre}
if Bitmap = 0 then
Bitmap := CreateCompatibleBitmap(Canvas.Handle, Width, Height);
Rect.top := 0;
Rect.bottom := Height;
Rect.left := (Width - 10) div 2;
Rect.Right := Rect.Left + 10;
SelectObject(MemDC, OldBmp);
end;
if Shadow then PaintShadow;
PaintTree;
end;
@Bactérius
Oui, cette instruction BitBlt est utile, je l'avais oubliée. Mais je suis dans une réflexion plus large et méthodologique car j'ai les étiquettes, mais aussi le reste. Ce qui voudrait dire qu'il me faut peut-être tout redessiner ou presque et pas seulement les étiquettes...
La seule constante est la dimension de l'arbre. Si elle est modifiée, je redessine tout...
Mais peut-être qu'en voulant économiser de la complexité de programmation, je ne vais pas beaucoup gagner en temps d'exécution. Le mieux serait alors de tout redessiner!
@ Cirec
C'est bon pour l'effacement.
Par contre je ne comprends pas ta phrase: "et pour finir tu peux appeler? alors "Invalidate" partout à la place de "Paint" comme dans "SetEtiquette"
"Invalidate" est plus performant que "Paint"
Invalidate? qu'est-ce que c'est en fait? Une méthode qui oblige à redessiner?
Cordialement
Merci Mauricio pour ces précisions!
Oui, les paint répétitifs, c'est un problème!
Invalidate = à retenir!
j'en profite pour vous redemander un conseil pour les dessins.
J'ai un graphique assez compliqué (B-arbre) à redessiner chaque fois qu'un paramètre change :
noeud, couleur, forme étiquette, forme des liens...
Bref des multitudes de détails.
a votre avis, est-il plus performant de tout redessiner ou de ne redessiner que la zone pixel à modifier en faisant des sortes de couches (bitmap) qu'il faut ensuite superposer?
Il me semble Mauricio que tu avais opté pour cette solution (dans un de tes codes) de sous-couches (calques) que l'on superpose!!!??
- Si je redessine tout, je dessine selon les options dans la méthode PaintTree :
procedure TTreeShape.PaintTree;
begin
With Canvas do
begin
Canvas.Brush.Color := FBrush.Color;
Canvas.Pen.Color := FPen.Color;
Case FEtiquette of
teCircle : DrawEtiqCircle;
teEllipse : DrawEtiqEllipse;
...
end;
Case FConnexion of
tcSolid : DrawConnectSolid;
tcShortDodded: DrawConnectShortDodded;
...
end;
...
end;
end;
C'est dans les procédures Draw que je réalise les différents graphiques qui vont composer le Bitmap final.
Merci beaucoup Mauricio pour ces bonnes précisions...
Je vais méditer sur tes réflexions et revoir ton compo TcyColorMatrix qui m'inspirera peut-être!
Bien à toi
Jean_Jean