jnmchl
Messages postés63Date d'inscriptiondimanche 16 octobre 2005StatutMembreDernière intervention13 novembre 2009
-
23 févr. 2008 à 21:13
cs_cantador
Messages postés4720Date d'inscriptiondimanche 26 février 2006StatutModérateurDernière intervention31 juillet 2021
-
27 févr. 2008 à 10:58
Je fais une appli destinée à piloter des réseaux de fluides dans une usine.
L'interface principale est un scrollbox comportant un graphique qui dessine le synoptique de chaque tuyau.
Ce graphique est interactif : les couleurs que doivent prendre les différents segments donne une information sur le fluide dans le tuyau donné, et en cliquant dessus l'utilisateur peut commander des vannes et des pompes.
J'ai donc créé un composant personalisé TSegment qui gère tout ce dont j'ai besoin : 3 propriétés couleurs, des propriétés d'indice etc ...
et les évènements on click, onclickdroit ...
L'aspect graphique correspond au cahier de charge.
Le gros de l'application fonctionne, mais j'ai un problème de taille : le logiciel ne tourne pas en permanence sur les micros des utilisateurs et au démarrage de l'application elle lit un fichier de paramètres (style ini) qui contient tout le graphique : les coordonnées de chaque segment entres autres. Il y a pour l'instant près de 7000 segments existants et le temps d'initialisation dure près d'une minute ... ce qui est beaucoup trop long.
Vous pouvez chargez une partie du code où je fais une boucle qui génère 520 segments ... voyez le temps que ça met !
http://mapage.noos.fr/ferroweb/Project1.zip
En étudiant le phénomène (chronomètrage des étapes) :
- en mettant le scrollbox1.visible à false ça va beaucoup plus vite, mais ça reste trop long
- le ralentissement est de plus en plus important vers la fin quand il y a beaucoup de segments
- la part create et plus longue que la part formatage (procédure formate : calculs) et Paint (environ 3/4 - 1/4)
Que peut-on optimiser ?
Est-ce lié à un problème de mémoire ? de ressources ?
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 24 févr. 2008 à 20:57
Salut !
Hum oui en effet, y'a pas mal besoin d'optimiser tout ça !!!
J'ai déjà trouvé trois (voire quatre suivant comment on compte) gros gouffres à temps dans ton code :
[un peu] 1- Créer et détruire ta fonte à chaque fois au lieu de la conserver
[un peu plus] 2- La sur-utilisation de Regions pour donner une forme à tes composants prend un sacré paquet de temps à s'exécuter et en plus c'est pas super pratique.
[énorme gouffre !] 3- Tu as troooop de composants ! C'est tout simplement ça !
Car tu aurais du créer un unique composant qui contiendrait une liste d'objets (TObjectList ou simple tableau) (voire de records) et chacun d'entre eux contiendrait les infos sur ton tuyau.
Ton composant aurait alors deux rôles:
- Les stocker (il en contient la liste, via le tableau)
- Les dessiner (dans ce cas, une boucle for sur tout le tableau suffit à tout dessiner)
Et pour éviter de se casser les yeux, on dessine tout dans un tampon bitmap comme le précise Caribensila (surtout si c'est dans un TScrollBox), tampon qui est mis à jour à chaque fois que tes infos changent (et non pas à chaque appel de Paint).
Et si ça suffit pas, on met DoubleBuffered à True, ce qui nous fera un TripleBuffer en réalité, donc ça devrait largement convenir.
Donc, pour répondre à ton ultime question, le problème est plutôt au niveau des ressources (sur-utilisation d'objets lourds, surtout un TCustomControl, tu doubles presque la vitesse en remplaçait juste "=class(TcustomControl)" par "=class(TGraphicControl)"
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 24 févr. 2008 à 21:55
Comme je suis trop gentil ^^ je t'ai fait une unité d'exemple pour que tu voies comment faire.
Tout y est presque, il ne manque "que" tes procédures de calculs des coordonnées car je n'y comprenais pas grand chose.
Note bien la différence avec ta version !
Et n'hésites pas à demander des précisions, j'ai commenté un max mais bon c'est jamais super explicite.
Bonne lecture !
--------------------------------------------------------------------------------
unit Module;
type
(* Deux manières différentes de stocker tes coordonnées
mais identiques en mémoire (donc transtypage possible si besoin) *)
TSegmentCoords = array[0..1] of TPoint; // [0]:(X1,Y1) [1]:(X2,Y2)
TSegmentCoords2 = array[0..3] of Integer; // [0]:X1 [1]:Y1 [2]:X2 [3]:Y2
(* déclaration avancée de la classe de dessin *)
TSegmentDrawer = class;
(* Classe représentant un segment à dessiner *)
TSegment = class(TObject)
private
{ ID unique (technique de sioux pour pouvoir l'identifier à partir
de la position (X,Y) de la souris) }
FID: Word;
{ Référence vers le propriétaire }
FOwner: TSegmentDrawer;
{ Coordonnées (fournies par l'utilisateur) (correspond à tes FFX1, ... }
FUserCoords: TSegmentCoords;
{ Coordonnées calculées }
FCalcCoords: TSegmentCoords; // FX1, FY1, FX2, FY2
FNx: Integer;
FNy: Integer;
FACalc: TSegmentCoords; // A1x, A1y, A2x, A2y
FBCalc: TSegmentCoords; // B1x, B1y, B2x, B2y
FAMaxX: Integer;
FAMaxY: Integer;
FAMinX: Integer;
FAMinY: Integer;
FX: Integer;
FCad: Extended;
FCop: Extended;
FAlp: Extended;
{ Diverses données }
FA1, FA2: Boolean;
FEtiq: string;
FNum: string;
FTpe: string;
FInd: string;
public
constructor Create(AID: Word; AOwner: TSegmentDrawer);
procedure Format;
procedure PaintTo(ACanvas, AMapCanvas: TCanvas);
{ Propriétés }
property ID: Word read FID;
property Point1: TPoint read FUserCoords[0] write FUserCoords[0];
property Point2: TPoint read FUserCoords[1] write FUserCoords[1];
property A1: Boolean read FA1 write FA1;
property A2: Boolean read FA2 write FA2;
property NumId: string read FNum write FNum;
property Tpe: string read FTpe write FTpe;
property Etiq: string read FEtiq write FEtiq;
property Indice: string read FInd write FInd;
end;
(* Procédure de l'évènement de clic *)
TSegmentClickEvent = procedure (ASegment: TSegment) of object;
(* Classe permettant le dessin des segments *)
TSegmentDrawer = class(TGraphicControl)
private
{ Liste de segments }
FList: TObjectList;
{ Dernier ID affecté }
FLastID: Word;
{ Couleurs }
FCouleur: TColor;
FCouleurLbl: TColor;
FCouleurLigne: TColor;
{ Bitmap temporaire de dessin }
FTempBmp: TBitmap;
{ Bitmap permettant de relier des coordonnées à un segment }
FMapBmp: TBitmap;
{ Evènement de clic }
FOnSegmentClick: TSegmentClickEvent;
{ Getters }
function GetSegment(I: Integer): TSegment;
function GetSegmentCount: Integer;
{ Setter }
procedure SetCouleur(I: Integer; C: TColor);
protected
procedure Resize; override;
procedure Paint; override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
{ Ajoute un segment à la liste et le renvoit }
function AddSegment: TSegment;
{ Permet aux segments de se dessiner }
procedure Actualize;
published
property Couleur: TColor index 0 read FCouleur write SetCouleur;
property CouleurLabel: TColor index 1 read FCouleurLbl write SetCouleur;
property CouleurLigne: TColor index 2 read FCouleurLigne write SetCouleur;
end;
procedure TSegment.Format;
begin
{ Là tu fais tes calculs comme dans l'autre procédure mais de façon à ne PAS
utiliser les Regions (mais ce sera facile vu les polygones que tu crée) }
end;
procedure TSegment.PaintTo(ACanvas, AMapCanvas: TCanvas);
begin
{ Ici, tu vas dessiner ton segment en DOUBLE :
1- D'abord normalement pèour l'affichage, en dessinant dans ACanvas.
Tu peux utiliser FOwner.Couleur, ... pour mettre en forme.
2- Et ensuite uniquement les contours de ton tuyau (sans le texte)
que tu vas peindre avec une couleur ID et un contour ID,
pour pouvoir permettre ensuite de le retrouver avec ses
coordonnées lors du clic }
{ >> Exemple s'il s'agissait d'un rectangle (les membres utilisées sont bien
sûr là juste pour l'exemple aussi) }
// Dessin dans la carte (map)
AMapCanvas.Pen.Color := TColor(FID); // on prend la couleur "ID"
AMapCanvas.Pen.Color := TColor(FID); // meme si en réalité c'est un nombre
AMapCanvas.Rectangle(Rect(FCalcCoords[0], FCalcCoords[1]));
// on dessine le même rectangle, mais plein cette fois et avec la couleur "ID"
end;
{
**************************** TSegmentDrawer **********************************
}
constructor TSegmentDrawer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{>> Création des objets }
FList := TObjectList.Create(True);
FTempBmp := TBitmap.Create;
FMapBmp := TBitmap.Create;
destructor TSegmentDrawer.Destroy;
begin
FMapBmp.Free;
FTempBmp.Free;
FList.Free;
inherited Destroy;
end;
function TSegmentDrawer.AddSegment: TSegment;
begin
{>> Crée un segment avec son identifiant unique (son numéro) }
Inc(FLastID);
Result := TSegment.Create(FLastID, Self);
end;
procedure TSegmentDrawer.Resize;
begin
inherited Resize;
{>> On dimentionne nos bitmaps comme le composant }
FTempBmp.Width := ClientWidth;
FTempBmp.Height := ClientHeight;
FMapBmp.PixelFormat := pf24Bit;
FMapBmp.Width := ClientWidth;
FMapBmp.Height := ClientHeight;
FMapBmp.PixelFormat := pf16Bit;
{>> Actualisation }
Actualize;
end;
procedure TSegmentDrawer.Actualize;
var
I: Integer;
begin
{>> Efface tout }
FTempBmp.Canvas.Brush.Color := Color;
FTempBmp.Canvas.FillRect(FTempBmp.Canvas.ClipRect);
FMapBmp.Canvas.Brush.Color := Color;
FMapBmp.Canvas.FillRect(FMapBmp.Canvas.ClipRect);
{>> Boucle pour que tous les segments se dessinent }
for I := 0 to GetSegmentCount - 1 do
GetSegment(I).PaintTo(FTempBmp.Canvas, FMapBmp.Canvas);
end;
procedure TSegmentDrawer.Paint;
begin
{>> Dessine tout simplement le bitmap }
Canvas.Draw(0, 0, FTempBmp);
end;
procedure TSegmentDrawer.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var
I, ID: Word;
begin
{>> Recherche du pixel qui nous intéresse
qui va nous donner l'ID du segment cliqué }
ID := Word(FMapBmp.Canvas.Pixels[X, Y]);
{>> Si on tombe sur un segment (ID <> 0) alors évènement
(après recherche du concerné dans la liste) }
if (ID <> 0) and Assigned(FOnSegmentClick) then
begin
for I := 0 to GetSegmentCount - 1 do
if GetSegment(I).ID = ID then
begin
FOnSegmentClick(GetSegment(I));
Break;
end;
end;
end;
function TSegmentDrawer.GetSegment(I: Integer): TSegment;
begin
Result := FList[I] as TSegment;
end;
function TSegmentDrawer.GetSegmentCount: Integer;
begin
Result := FList.Count;
end;
procedure TSegmentDrawer.SetCouleur(I: Integer; C: TColor);
begin
{>> Modifie }
case I of
0: FCouleur := C;
1: FCouleurLbl := C;
2: FCouleurLigne := C;
end;
cs_Delphiprog
Messages postés4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 24 févr. 2008 à 22:17
Je rejoins l'analyse de mes éminents confrères et j'ajoute que tu devrais t'intéresser au design pattern poids mouche (flyweight en anglais) car il correspond à la mise en oeuvre proposée par Florenth dans son troisième paragraphe (l'énorme gouffre) et exactement à ta problématique de surconsommation de composants avec leurs cortèges de méthodes.
Illustration sur le site DoFactory.
May Delphi be with you !
<hr color="#008000" />
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
jnmchl
Messages postés63Date d'inscriptiondimanche 16 octobre 2005StatutMembreDernière intervention13 novembre 20091 25 févr. 2008 à 13:23
Merci pour toutes vos réponses déjà, je vais essayer de les mettre en oeuvre, je risque de vous re contacter pour ce que je n'ai pas compris ...
Toutefois, je ne sais pas si tout est applicable à l'ensemble du logiciel, car la petite démo ne représente pas l'usine (qui n'est pas à gaz ) contrairement à mon logiciel qui commence sérieusement à le devenir !
Aussi si vous pouviez me confirmer ou infirmer mes questions :
tout dessiner dans un TBitmap en mémoire :
- oui mais : est-ce que l'interactivité fonctionne ? click + click droit ?
- attention certains segments doivent clignoter est-ce rapide ?
utiliser OpenGL :
- heu oui, je vais le proposer à la maîtrise d'ouvrage ... mais il faut que je me forme d'abord à la 3D ... je crains devoir demander un délai non raisonnable
Il aurait été préférable de dériver tes TSeg à partir de la classe TGraphicControl.
- j'avais essayé, mais je crois que je me suis cassé le nez sur :
la manière de dessiner dans le canvas, la récupération des clics et la forme à donner au logiciel (régions). Mais si ça accélère la chose je suis preneur ... mais je revoir ce qui bloquait.
Créer et détruire ta fonte à chaque fois au lieu de la conserver
- On m'avait dit de toujours libérer les ressources ? En fait la police inclinée que je crée prend l'angulation du segment en fonction de x1,y1 et x2,y2, elle est donc particulière à chaque segment.
La sur-utilisation de Regions pour donner une forme à tes composants
- Oui mais le composant doit avoir la forme du segment de tuyau : s'il y a deux tuyaux parallèles inclinés à 45° : l'enveloppe carré ne permet pas de cliquer sur le bon tuyau ... pour les test sur A1 et A2 il y a certes une part d'esthétique mais aussi la visualisation d'un séparation.
Tu as troooop de composants ! C'est tout simplement ça !
- ben pour l'instant il y a 7000 segments (et c'est pas fini ...) chaque segment doit être cliquable et avoir ses propres couleurs ... Est-ce que le fonctionnement final sera similaire avec TObjectList ou simple tableau.
Autre précision de ma part :
Quand je parle de lenteur d'exécution c'est surtout au démarrage la phase d'initialisation pendant laquelle je créé les composants dans mon scrollbox. Le reste : animation : changement de couleurs, clignotement, et action de la souris fonctionne bien.
Le fichier qui donne le schéma est issu d'un autre logiciel dont je ne suis pas l'auteur : il servait à leur précedent logiciel de commande et de contrôle, les segement peuvent avoir des coordonnées x1,x2 y1,y2 indépendant de ce que moi je voudrais.
Merci encore à tous, je vais essayer toute vos méthodes ... avant de cocher "réponse acceptée", mais merci encore une fois.
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 25 févr. 2008 à 14:01
Leurs segments sont définis par deux points : (x1,y1) et (x2,y2)
Cela veut dire que c'est toi même qui leur donne leur épaisseur, qui est donc constante ?
Sinon, pour répondre à tes questions :
"est-ce que l'interactivité fonctionne ? click + click droit ?"
=> C'est vrai que ça va compliquer un peu la chose, la preuve dans mon unité d'exemple, mais ça reste faisable bien sûr.
"attention certains segments doivent clignoter est-ce rapide ?"
=> Tu ne nous avais pas décrit ce besoin, dans ce cas il va falloir penser à distinguer deux cas :
- Celui où tu as besoin de tout dessiner (une fois au début puis si jamais tu changes la taille du composant)
- Celui où tu ne redessines que le segment qui clignote. Dans ce cas là, ce sera rapide.
"Utiliser OpenGL"
=> Je te le déconseille, sauf si jamais tu as déjà des connaissances sûres. apprendre OpenGL pour ce genre de contexte me parait énorme.
"sur les TGRaphiControl: j'avais essayé, mais je crois que je me suis cassé le nez"
=> Étrange, car si tu reprends ton code et que tu changes juste la déclaration, tout fonctionne aussi et tu gagnes (un petit peu) en vitesse.
"On m'avait dit de toujours libérer les ressources"
=> Oui, mais rien ne t'empèches de la créer au tout début de ton programme et de la libérer à la toute fin ! ça consomme un peu de mémoire, mais c'est bien pratique !
"Oui mais le composant doit avoir la forme du segment de tuyau"
=> C'est justement pour cela que passer par un composant par tuyau n'est pas une super idée. Avec le design pattern que te décris Delphiprog, tu n'auras plus ce problème du tout.
"Est-ce que le fonctionnement final sera similaire avec TObjectList ou simple tableau"
=> Le fonctionnement sera strictement identique. La seule chose qui changera, c'est l'intérieur du composant. Mais ça n'aura aucune influence sur l'aspect extérieur donc ton code précédent sera compatible. (si toutefois tu l'as codé en respectant les règles de la POO, ce qui n'est pas tout à fait le cas dans l'exemple que tu donnes en zip)
"Quand je parle de lenteur d'exécution c'est surtout au démarrage la phase d'initialisation pendant laquelle je créé les composants dans mon scrollbox. Le reste : animation : changement de couleurs, clignotement, et action de la souris fonctionne bien."
=> C'est normal, mais tu auras toujours un temps de chargement significatif (même avec ces changements) car dessiner 7000 segments prend toujours un temps fou (sauf en OpenGL mais c'est une autre histoire). Le gain sera au niveau de la la gestion des ressources qui sera beaucoup plus légère (d'où le nom du DP, flyweight)
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 201918 25 févr. 2008 à 14:41
Re,
«
"Utiliser OpenGL"
=> Je te le déconseille, sauf si jamais tu as déjà des connaissances sûres. apprendre OpenGL pour ce genre de contexte me parait énorme. »
Flo a raison.
Mais il existe cependant une alternative bien adaptée à cette appli et abordable : GLScene.
Je m'y étais intéressé quand j'ai commencé Delphi (eh oui! La charrue avant les boeufs.. ;) sans trop de difficultés. Un pro devrait très vite prendre en main GLScene, je pense. D'autant qu'on trouve de nombreux et excellents tutos.
Et les résultats obtenus sont très vite bluffants en rendu et rapidité!
Ceci dit, je ne voudrais pas d'envoyer dans une voie sans issue. En effet, je ne me souviens plus si les objets d'une scène sont cliquables...
J'ai aussi pensé à une autre idée facile à explorer pour le chargement initial : WriteComponentResFile() et ReadComponentResFile().
Il me semble d'ailleur que Delphiprog est l'auteur d'un tuto sur cette technique de mise en flux de compos. Je crois que ça devrait être plus rapide mais c'est à vérifier.
- le label 3 copie l'ID de chaque segement sur lequel on clique
- quelques segments montre les 3 fonctions de couleurs clignotantes
- et un autre segment change de couleur fixe avec les boutons 2,3 et 4.
Ah oui j'oubliai : les segments prennent aussi un hint.
et ...
Leurs segments sont définis par deux points : (x1,y1) et (x2,y2)
Cela veut dire que c'est toi même qui leur donne leur épaisseur, qui est donc constante ?
Oui, sauf cas particulier : la variable kd peut changer en fonction du zoom, la aussi le refraichissement prend une minute, mais ce n'est pas une manipulation courante. Mais quoi qu'il en soit, l'épaisseur reste constante pour un zoom donné ... c'est ce "rectangle" qui est calculé dans ma procedure formate et parfois il faut ajouter un disque au bout pour une meilleur esthétique de dessin des angles .
Voilà, voilà ...
Ma méthode (même si je comprends bien qu'elle n'est pas pro) évite de tout redessiner car je peux interagir avec chaque composant très rapidement et très facilement.
Je ne cherche pas avoir une accélération si elle risque de ralentir l'affichage par la suite.
jnmchl
Messages postés63Date d'inscriptiondimanche 16 octobre 2005StatutMembreDernière intervention13 novembre 20091 25 févr. 2008 à 18:30
Rebonjour,
Pourquoi j'ai utilisé TCustomControl au lieu de TGraphicControl ? (ou pourquoi je m'étais cassé le nez avec TGraphicControl ...)
parce que j'ai besoin du SetWindowRegion (peut-on l'utiliser dans TGraphic Control ?)
Pourquoi j'ai utilisé les Régions ?
parce que dans le cas des tuyaux parallèles à 30° ou 45° (ou qui se croisent) je n'arrive pas à les distinguer les uns des autres.
Voir l'image :http://mapage.noos.fr/ferroweb/tuyaux%20paralleles.jpg
à rajouter à ma boucle pour un segment supplémentaire //
TSeg := TSegment.Create(Form1);
TSeg.X1:= x*60+20;
TSeg.Y1:= y*30+10;
TSeg.X2:= x*60+40;
TSeg.Y2:= y*30+30;
TSeg.A1:= true;
TSeg.A2:= false;
TSeg.Tpe:='Ok';
TSeg.NumId:=IntToStr(x)+IntToStr(y)+'e';
TSeg.Etiq:=IntToStr(x)+IntToStr(y)+'bis';
TSeg.Indice:='bis';
TSeg.Formate;
Par exemple les segments 00 et 00bis sont bien deux segments différents
Donc jusque-là, la proposition d'utiliser TGraphicControl ne me permet pas celà.
La possibilité du composant unique qui contiendrait une liste d'objets me séduit aussi ... mais je ne sais pas par quel bout commencer, j'y réfléchi.
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 25 févr. 2008 à 19:02
En effet, le TGraphicControl ne permet pas l'usage de régions, c'est ce qui le rend léger d'ailleurs (entres-autres).
Pour commencer avec le composant unique, je t'ai posté le code d'une unité qui pourrait être un début d'inspiration. Si ça peut t'aider...
Mais comme seul toi connais tes capacités, tes contraintes de projet, etc... on va avoir du mal à te renseigner.
Cela dit, nous restons ouverts à toute question, c'est bien le but du forum.
jnmchl
Messages postés63Date d'inscriptiondimanche 16 octobre 2005StatutMembreDernière intervention13 novembre 20091 25 févr. 2008 à 22:27
Re,
J'ai testé l'utilisation de ObjectList, j'ai donc remplacé :
TSeg := TSegment.Create(Form1);
TSeg.X1:= x*60+20;
TSeg.Y1:= y*30+10;
TSeg.X2:= x*60+40;
TSeg.Y2:= y*30+30;
TSeg.A1:= true;
TSeg.A2:= false;
TSeg.Tpe:='Ok';
TSeg.NumId:=IntToStr(x)+IntToStr(y)+'e';
TSeg.Etiq:=IntToStr(x)+IntToStr(y)+'bis';
TSeg.Indice:='bis';
TSeg.Name:='X'+TSeg.NumId;
TSeg.Formate;
par
TSeg := TSegment.Create(Form1);
ObjectList.Add(TSeg);
TSegment(ObjectList.Last).X1:= x*60+20;
TSegment(ObjectList.Last).Y1:= y*30+10;
TSegment(ObjectList.Last).X2:= x*60+40;
TSegment(ObjectList.Last).Y2:= y*30+30;
TSegment(ObjectList.Last).A1:=true;
TSegment(ObjectList.Last).A2:= false;
TSegment(ObjectList.Last).Tpe:='Ok';
TSegment(ObjectList.Last).NumId:=IntToStr(x)+IntToStr(y)+'e';
TSegment(ObjectList.Last).Etiq:=IntToStr(x)+IntToStr(y);
TSegment(ObjectList.Last).Indice:='a';
TSegment(ObjectList.Last).Name:='X'+TSeg.NumId;
TSegment(ObjectList.Last).Formate;
ça marche, mais bien que ça facilite l'accès au composant, au démarrage je ne mesure pas de gain de temps perceptible ... d'où mes questions :
- l'ai-je bien utilisé ?
- je ne sais pas si pour mon projet ça s'impose car les composants ne sont pas détruits en cours d'exécution.
- je ne comprends pas vraiment le fait que ça réduit mon nombre de composants (il y autant de composants sauf qu'il sont dans une liste) ?
Merci à tous en particulier florenth (pour ta patience) avant de recopier simplement ton code j'essaie de le comprendre, je m'y attache demain. S'il y a un moyen de naviguer dans un dessin et qu'au dessus de chaque segment on puisse d'une manière ou une autre récupérer un ID à tous les coups (en particulier les segments parallèles à 45° où les enveloppes rectangulaires se superposent) peut-être qu'avec ton code la solution sera proche ... Flash (de MacroMédia) le permet bien.
jnmchl
Messages postés63Date d'inscriptiondimanche 16 octobre 2005StatutMembreDernière intervention13 novembre 20091 25 févr. 2008 à 22:27
Re,
J'ai testé l'utilisation de ObjectList, j'ai donc remplacé :
TSeg := TSegment.Create(Form1);
TSeg.X1:= x*60+20;
TSeg.Y1:= y*30+10;
TSeg.X2:= x*60+40;
TSeg.Y2:= y*30+30;
TSeg.A1:= true;
TSeg.A2:= false;
TSeg.Tpe:='Ok';
TSeg.NumId:=IntToStr(x)+IntToStr(y)+'e';
TSeg.Etiq:=IntToStr(x)+IntToStr(y)+'bis';
TSeg.Indice:='bis';
TSeg.Name:='X'+TSeg.NumId;
TSeg.Formate;
par
TSeg := TSegment.Create(Form1);
ObjectList.Add(TSeg);
TSegment(ObjectList.Last).X1:= x*60+20;
TSegment(ObjectList.Last).Y1:= y*30+10;
TSegment(ObjectList.Last).X2:= x*60+40;
TSegment(ObjectList.Last).Y2:= y*30+30;
TSegment(ObjectList.Last).A1:=true;
TSegment(ObjectList.Last).A2:= false;
TSegment(ObjectList.Last).Tpe:='Ok';
TSegment(ObjectList.Last).NumId:=IntToStr(x)+IntToStr(y)+'e';
TSegment(ObjectList.Last).Etiq:=IntToStr(x)+IntToStr(y);
TSegment(ObjectList.Last).Indice:='a';
TSegment(ObjectList.Last).Name:='X'+TSeg.NumId;
TSegment(ObjectList.Last).Formate;
ça marche, mais bien que ça facilite l'accès au composant, au démarrage je ne mesure pas de gain de temps perceptible ... d'où mes questions :
- l'ai-je bien utilisé ?
- je ne sais pas si pour mon projet ça s'impose car les composants ne sont pas détruits en cours d'exécution.
- je ne comprends pas vraiment le fait que ça réduit mon nombre de composants (il y autant de composants sauf qu'il sont dans une liste) ?
Merci à tous en particulier florenth (pour ta patience) avant de recopier simplement ton code j'essaie de le comprendre, je m'y attache demain. S'il y a un moyen de naviguer dans un dessin et qu'au dessus de chaque segment on puisse d'une manière ou une autre récupérer un ID à tous les coups (en particulier les segments parallèles à 45° où les enveloppes rectangulaires se superposent) peut-être qu'avec ton code la solution sera proche ... Flash (de MacroMédia) le permet bien.
cs_Delphiprog
Messages postés4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 25 févr. 2008 à 23:01
Petite rectification de nature à alléger le code :
Sachant que TSeg représente le dernier élément ajouté à Objectlist, pourquoi vouloir transtyper sur chaque ligne ?
Ainsi, il suffit d'écrire :
TSeg := TSegment.Create(Self);
ObjectList.Add(TSeg);
with TSeg do
begin
X1:= x*60+20;
Y1:= y*30+10;
X2:= x*60+40;
Y2:= y*30+30;
A1:=true;
A2:= false;
Tpe:='Ok';
NumId:=IntToStr(x)+IntToStr(y)+'e';
Etiq:=IntToStr(x)+IntToStr(y);
Indice:='a';
Name:='X'+TSeg.NumId;
Formate;
end;
May Delphi be with you !
<hr color="#008000" />Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
cs_Delphiprog
Messages postés4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 25 févr. 2008 à 23:11
"...je ne comprends pas vraiment le fait que ça réduit mon nombre de
composants (il y autant de composants sauf qu'il sont dans une liste) ?"
Dans le design pattern flyweight, les composants de base ne stockent que des propriétés et pas de méthodes. Seul le composant aggrégateur contient les méthodes nécessaires. C'est également ce dernier composant qui gère la liste des objets "de base" et non une fiche ou une variable globale dans l'application.
Pour reprendre l'exemple classique, imaginons qu'on veuille dessiner les arbres d'une forêt. Est-ce que chaque arbre a besoin d'une méthode pour se dessiner ou est-ce qu'une objet forêt ne serait pas capable de dessiner chacun des arbres qu'elle contient ? Quand un grand nombre d'objets entrent en jeu (et c'est le cas dans ton application visiblement), la dernière formule est à privilégier. Cela na va pas forcément accélerer le traitement mais ça limitera la consommation de mémoire et éventuellement des problèmes de performance en limitant l'accès à la mémoire virtuelle sur disque qui est bien plus lente.
De plus, en implémentant ce pattern, il est infiniment plus facile de confier la partie dessin à une série de threads en encapsulant ce traitement dans une seule et unique classe : la forêt pour reprendre l'illustration ci-dessus.
May Delphi be with you !
<hr color="#008000" />Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 201918 26 févr. 2008 à 00:09
@Delphiprog
Ah! C'est ça le design pattern?
Je pensais que c'était un truc destiné aux pros du développement, moi...
Tu ne pourrais pas nous donner quelques bons liens pour s'initier à cette technique (en français de préférence)?
Jadis, j'avais codé un truc passionnant pour une copine qui étudiait la biologie. C'était sur la génétique (plutôt l'étude de l'«évolution») et j'avais des populations de milliers d'objets qui se "reproduisaient". Bien sûr, au bout d'un moment, la population n'évoluait plus tellement c'était lent à cause du nombre... :)))
Mais je reprendrais bien ce code que j'avais abandonné avec cette technique qui me semble très bien adaptée, comme au cas de jnmchl...
cs_Delphiprog
Messages postés4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 26 févr. 2008 à 00:25
@caribensila : si tu parles de l'exemple de la forêt, oui, c'est ça. Maintenant, il reste à mettre en oeuvre dans un langage donné. Cela dit, ce n'est pas le pattern le plus compliqué non plus. Le truc en biologie pour ta copine, ne me dis pas que tu as sacrifié ton corps à la science ??? Remarque que ça pourrait expliquer bien des choses, en y réfléchissant bien.
Bon j'arrête là le délire et je prie jnmchl de bien vouloir accepter mes excuses pour mes posts un peu hors-sujet.
Pour me faire pardonner, je lui propose de réaliser un tuto sur le dp poids-mouche.
Que ceux que ça intéresse lèvent le doigt...
May Delphi be with you !
<hr color="#008000" />Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 26 févr. 2008 à 10:29
J'ai vingt doigts et je les lève tous pour le tuto de Delphiprog !
@Delphiprog :
L'objet flyweight ne doit pas avoir de méthodes ?
Ca ne me parait pas super logique. Personnellement, dans ma classe TArbre, j'aurais mis une méthode pour qu'il se dessine dans la forêt.
Après tout, que la méthode doit dans TForet ou TArbre, ça ne consomme pas plus de mémoire, ni de temps CPU (le bytecode des méthodes est chargé en mémoire une seule et bonne fois pour toute, par classe et non pas par objet).
Et en plus, on pourrait dessiner autre chose que des TArbre dans le TForet (imagine un TArbreBrule après un incendie...)
Sinon, quitte à ne pas mettre de méthode, autant utiliser un TList avec des record, non ?
[ne t'embêtes pas à répondre si tu avais prévu de parler de ça dans ton tuto, ça te ralentirait dans ta rédaction !! ;-)]
@jnmchl: ne recopies pas (trop) mon code car il n'est pas fonctionnel (il manque pas mal de trucs).
Et j'ai aussi oublié un FList.Add(Result) à la fin de TSegmentDrawer.AddSegment().