Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Dag Tombo_incTombo_inc schreef op vrijdag 18 november 2005 @ 21:23:
beste mensen,
Je zet unit A in de uses clause van unit B en wel in de interface sectie. Je zet unit B in de uses clause van unit A en wel in de implementation sectie.nu is mijn probleem dat ik vanuit de method van mijn canvasobject, de canvas van mijn formulier niet aan kan spreken.
mijn canvasclass staat in een losse unit. als ik dan refereer naar de unit van mijn formulier middels een uses clause, dan krijg ik een fout van een "circular reference". ik weet dus niet hoe ik mijn parent object aan kan spreken.
een mogelijke oplossing is om de parent zelf het object te laten tekenen door gewoon alle benodigde properties uit te lezen van zijn child. maar dat lijkt me niet echt een nette oplossing. ik wil dus dat het object zelf in staat is om zichzelf op de parents canvas te tekenen.
weet iemand hoe ik dit kan realiseren?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| unit A; interface uses B; type TMijnButton = class(TButton); var EenLabel: TMijnLabel; implementation end. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| unit B; interface type TMijnLabel = class(TLabel); implementation uses A; procedure Test(EenButton: TMijnButton); begin EenLabel.Caption := EenButton.Caption; end; end. |
De aanpak die je kiest lijkt me overigens nogal ingewikkeld. Als je op het canvas alle objecten moet tekenen, wat gebeurt er dan als er een object wordt verplaatst? Ga je dan alles opnieuw op je canvas tekenen? En wat doe je als het Form geresized wordt? Ga je dan ook alles opnieuw tekenen? Kun je niet veel beter ieder tekenobject in een eigen control gieten die je op het canvas plaatst?
[ Voor 6% gewijzigd door Tomatoman op 18-11-2005 22:43 ]
Een goede grap mag vrienden kosten.
wat bedoel je precies met ieder object in een eigen control gieten?
ik hou nu gewoon een lijst bij met alle objecten die getekend moeten worden. en door middel van
het "invalidate" commando zorg ik dat het form steeds geupdate is/wordt. mis ik hier iets essentieels of kan het veel makkelijker/beter? (ik heb namelijk niet heel veel ervaring met tekenen in delphi)
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Wat jij doet werkt gewoon, het is zeker niet fout.Tombo_inc schreef op vrijdag 18 november 2005 @ 23:49:
wat bedoel je precies met ieder object in een eigen control gieten?
ik hou nu gewoon een lijst bij met alle objecten die getekend moeten worden. en door middel van
het "invalidate" commando zorg ik dat het form steeds geupdate is/wordt. mis ik hier iets essentieels of kan het veel makkelijker/beter? (ik heb namelijk niet heel veel ervaring met tekenen in delphi)
De performance kan echter problematisch worden als er veel getekend moet worden. Bovendien kan het heel complex worden. Stel dat je 100 vierkanten op je canvas wilt weergeven en dat je canvasgrootte afhankelijk is van de venstergrootte van het main form. Als de gebruiker het main form dan resizet, moeten die 100 vierkanten tijdens het resizen telkens opnieuw worden getekend (als je canvas een pixel breder of hoger wordt, leidt dat immers tot een OnPaint event). Dat is inefficiënt en leidt tot schermflikkering.
Een ander probleem kan zijn dat het allemaal moeilijk onderhoudbaar kan gaan worden. Straks wil je er misschien ook cirkels bij kunnen plaatsen. En nog wat later ook bitmaps. Of je wilt dat de de vierkanten ook rood en oranje kunnen worden, maar de cirkels alleen groen. Hoe ga je dat allemaal bijhouden in je tekenlijstje?
Een oplossing zou kunnen zijn dat je een descendant van TGraphicControl schrijft, die als base class geldt voor al jouw tekenobjecten. In TGraphicControl definieer je bijvoorbeeld een Color property. Daarna maak je een paar descendants van je TGraphicControl descendant, bijvoorbeeld TVierkant en TCirkel. Nu het eigenlijk tekenwerk. In plaats van allerlei dingen op een canvas te tekenen, plaats je een paar TVierkant en TCirkel objecten op het canvas. Als het canvas nu van maat wijzigt, regelt Delphi achter de schermen voor je dat alleen die objecten opnieuw getekend worden die ook daadwerkelijk veranderen door het resizen.
Een goede grap mag vrienden kosten.
het enige verschil met jouw oplossing is dus dat ik een TGraphicControl moet gebruiken als baseclass omdat delphi dan het tekenwerk voor me regelt. en dat ik op de canvas van die TGraphicControl mijn figuur moet tekenen. (correct me if i'm wrong)
nu is mijn vraag, hoe kan ik rechtstreeks een TGraphicControl op de Canvas van het forumlier plaatsen? (mssn een domme vraag maar ik kan het niet vinden)
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Aha, volgens het boekje.Tombo_inc schreef op zaterdag 19 november 2005 @ 22:01:
ok dat snap ik. dat is dus bijna hetzelfde princiepe als wat ik nu heb. ik heb een baseclass voor alle grafische objecten. en iedere vorm is daar een afgeleide van. alle grafische objecten hou ik bij in een TList.
Correct.[...] en teken ik inderdaad met een onpaint.
het enige verschil met jouw oplossing is dus dat ik een TGraphicControl moet gebruiken als baseclass omdat delphi dan het tekenwerk voor me regelt. en dat ik op de canvas van die TGraphicControl mijn figuur moet tekenen. (correct me if i'm wrong)
Je plaatst de control niet op het canvas van het formulier, maar je plaatst de control op een andere control (via de Parent property). Je tekent vervolgens iedere control op zijn eigen canvas en laat het canvas van het formulier voor wat het is.nu is mijn vraag, hoe kan ik rechtstreeks een TGraphicControl op de Canvas van het forumlier plaatsen? (mssn een domme vraag maar ik kan het niet vinden)
Als voorbeeld een eenvoudig stukje code. Op een form staan een TScrollBox - die ScrollBox heet - en een TButton. Telkens als op de button wordt geklikt, wordt er in de scrollbox een TPaintBox bijgeplaatst. Het tekenwerk van iedere paint box gebeurt met de OnPaint handler van de paint box. De scrollbox is een tamelijk willekeurig keuze om paint boxen in te plaatsen, je kunt net zo goed alles op het TForm gooien of op een TPanel. Alle paint boxen worden bijgehouden in een TComponentList (waar ik in dit voorbeeldje verder niets mee doe).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
| unit Unit1; interface uses Classes, Graphics, Controls, Forms, StdCtrls, ExtCtrls, Contnrs; type TForm1 = class(TForm) ScrollBox: TScrollBox; Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); private FPaintBoxList: TComponentList; procedure PaintBoxPaint(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Randomize; FPaintBoxList := TComponentList.Create(True); end; procedure TForm1.FormDestroy(Sender: TObject); begin { Als FPaintBoxList vernietigd wordt, worden meteen alle PaintBoxes in de list vernietiegd. } FPaintBoxList.Free; end; procedure TForm1.Button1Click(Sender: TObject); var Box: TPaintBox; begin Box := TPaintBox.Create(nil); with Box do begin { Kleur van de brush kiezen en de OnPaint handler toewijzen } Tag := Random($FFFFFF); // Tag gebruiken voor de random te kiezen kleur OnPaint := PaintBoxPaint; { Ergens random in de scrollbox plaatsen } Left := Random(ScrollBox.ClientWidth - Width); Top := Random(ScrollBox.ClientHeight - Height); Parent := ScrollBox; end; FPaintBoxList.Add(Box); end; procedure TForm1.PaintBoxPaint(Sender: TObject); var Box: TPaintBox; begin Box := Sender as TPaintBox; with Box do begin Canvas.Brush.Color := TColor(Tag); // Kleur staat in de tag Canvas.FillRect(ClientRect); // De complete paint box inkleuren end; end; end. |
Een goede grap mag vrienden kosten.
wat jij dus doet is onclick een nieuw object maken. die volproppen met wat waarden en waar het om gaat, hem een parent toewijzen. en dan zou het dus goed moeten zijn
hartstikke bedankt alvast, ik ga het meteen proberen.
er is nog een ding dat ik niet snap. en wel deze regel:
1
| Box := Sender as TPaintBox; |
zou je die eens uit kunnen leggen?
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
In an event handler, the Sender parameter indicates which component received the event and therefore called the handler. Sometimes it is useful to have several components share an event handler that behaves differently depending on which component calls it. You can do this by using the Sender parameter in an if...then...else statement. For example, the following code displays the title of the application in the caption of a dialog box only if the OnClick event was received by Button1.
Delphi:
1 2 3 4 5 6 7 8 procedure TMainForm.Button1Click(Sender: TObject); begin if Sender = Button1 then AboutBox.Caption := 'About ' + Application.Title else AboutBox.Caption := ''; AboutBox.ShowModal; end;
Een goede grap mag vrienden kosten.
Verwijderd
Volgens mij is bovenstaande niet waar, of op z'n minst een non-argument. Immers, als een gebruiker het mainform resized, zullen altijd en in elk geval, ongeacht van de implementatie, alle vierkanten opnieuw getekend moeten worden.tomatoman schreef op zaterdag 19 november 2005 @ 00:20:
[...]
De performance kan echter problematisch worden als er veel getekend moet worden. Bovendien kan het heel complex worden. Stel dat je 100 vierkanten op je canvas wilt weergeven en dat je canvasgrootte afhankelijk is van de venstergrootte van het main form. Als de gebruiker het main form dan resizet, moeten die 100 vierkanten tijdens het resizen telkens opnieuw worden getekend (als je canvas een pixel breder of hoger wordt, leidt dat immers tot een OnPaint event). Dat is inefficiënt en leidt tot schermflikkering.
Het schermflikkeren is te voorkomen door bij het eerste object wat je update ervoor te zorgen dat wijzigingen pas aan het eind doorgevoerd worden.
Voor de rest sluit ik me aan bij je opmerkingen, maar vond dit ene puntje toch relevant, sorry
[ Voor 31% gewijzigd door Verwijderd op 21-11-2005 23:39 ]
Als je al het tekenwerk op één canvas zou doen, zou bij elke vergroting van het canvas het complete canvas opnieuw getekend moeten worden. Bij het gebruik van afzonderlijke controls met elk hun eigen canvas gaat dit echter veel subtieler, lees: efficiënter.Verwijderd schreef op maandag 21 november 2005 @ 23:38:
[...]
Volgens mij is bovenstaande niet waar, of op z'n minst een non-argument. Immers, als een gebruiker het mainform resized, zullen altijd en in elk geval, ongeacht van de implementatie, alle vierkanten opnieuw getekend moeten worden.
Neem nog even mijn voorbeeldcode. De TScrollBox is ingesteld op Align = alClient en in de OnPaint handler heb ik drie regels code toegevoegd:
69
70
71
| { Piepen als de eerste paint box getekend wordt } if Box = FPaintBoxList[0] then Beep; |
Telkens als het eerste vierkant opnieuw getekend wordt, hoor je nu een piep. In onderstaande schermafdruk is het eerste vierkante paars:

Als je nu het form met de cursor breder maakt, hoor je niets. Blijkbaar wordt het paarse vierkant niet opnieuw getekend. Maar als je het form eerst dusdanig smaller maakt dat het paarse vierkant nog maar gedeeltelijk zichtbaar is en het form daarna weer verbreedt, hoor je wel degelijk een hele rij piepjes. Blijkbaar wordt het paarse vierkant nu wel opnieuw getekend.
Hoe kan dit? Achter de schermen zullen alleen de regions waar daadwerkelijk wat opnieuw getekend moet worden geïnvalideerd worden. In de schermafdruk kun je duidelijk zien dat bij het breder maken van het venster het paarse vierkant ongewijzigd blijft. Windows weet dit ook een zal daarom het vierkant niet invalideren, zodat er geen OnPaint event volgt.
Jaja hezik, het is dus echt efficiënter. Vooral omdat tekenwerk relatief veel CPU cycles kost.
Een goede grap mag vrienden kosten.
Verwijderd
-minder/geen opnieuw tekenen bij veranderingen in andere tekeningen
-geen opnieuw tekenen bij verplaatsen object (alleen de coordinaties van de container veranderen)
-geen opnieuw tekenen bij veranderen z-order (alleen de z-order van de containers veranderen)
Maar het gebruik van aparte containers heeft 1 groot nadeel:
-Sommige tekenfuncties hebben invloed op wat er al op de canvas staat getekend. Sommige vullen een bepaalde kleur in als ze die tekenkomen, of ze wisselen elke kleur om. Dit werkt natuurlijk niet als elk object zijn eigen container/canvas heeft. Dit effect zou je misschien nog kunnen simuleren, maar toch kan je dan niet garanderen dat alles hetzelfde uit zou zien als ze dezelfde canvas zouden gebruiken.
ik heb alleen een tweede probleem waar ik niet uit kom.
ik heb nu globaal gezien de volgende situatie.
een formulier:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,unit2; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } blaat: TMijnObject; constructor Create(AOwner: TComponent); override; end; var Form1: TForm1; implementation {$R *.dfm} constructor TForm1.Create(AOwner: TComponent); begin inherited Create(AOwner); self.blaat := TMijnObject.create(AOwner); end; end. |
en een bestand met een class erin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,ExtCtrls; type TMijnObject = class(TPaintBox) constructor Create(AOwner: TComponent); override; procedure MijnObjectPaint; end; var MijnObject: TMijnObject; implementation uses unit1; constructor TMijnObject.Create(AOwner: TComponent); begin inherited create(AOwner); //self.OnPaint := {??} { moet ik hier iets mee? } self.Left := 10; self.Top := 10; self.Width := 200; self.Height := 200; self.Canvas.MoveTo(20,20); // hier doet ie ook moeilijk self.Canvas.LineTo(100,100); // exeption: {control has no parent window } end; procedure TMijnObject.MijnObjectPaint; begin {hier moet dan een figuur op eigen canvas worden getekend maar ik krijg deze onpaint niet aan de gang.} self.Canvas.MoveTo(20,20); self.Canvas.LineTo(100,100); end; end. |
wat ik nu voor mekaar gestumperd probeer te krijgen is het volgende.
ik heb een form. dit form heeft een child object dat ik zelf gemaakt heb. het childobject is afgeleid van een TPaintBox. het childobject word aangemaakt op het moment dat het form ook gemaakt wordt. nu wil ik op de canvas van dat childobject een tekening maken, met behulp van het onpaint event. en omdat het een eigen control is en bij als child op het form staat, zou delphi hem nu ook moeten tekenen als ik alles goed begrepen heb. ik hoop dat ik het duidelijk uitgelegd heb.
mijn probleem is nu dat ik die onpaint niet getriggerd krijg of op zijn minst niet gekoppeld krijg aan mijn eigen procedure die dan uitgevoerd moet worden. ik heb voor mijn gevoel zo'n beetje alles geprobeerd, behalve het juiste dus
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Maar je kan ook een TPanel of nog beter: een TGraphicControl nemen en de Paint method overriden.
We adore chaos because we like to restore order - M.C. Escher
Verwijderd
Neenee tomatoman, het principe wat jij beschrijft is niet efficienter. In de praktijk blijkt die methode efficienter omdat de bouwers van Delphi/Windows wat optimalisatietruukjes hebben uitgehaald. DAT is de reden waarom het efficienter is, niet vanwege het onderliggende systeem.tomatoman schreef op dinsdag 22 november 2005 @ 00:02:
Jaja hezik, het is dus echt efficiënter. Vooral omdat tekenwerk relatief veel CPU cycles kost.
Niets weerhoudt je ervan diezelfde optimalisaties zelf toe te voegen.
Voor alle duidelijkheid: ik vind jouw methode ook beter, waarom zelf het wiel uitvinden als het er al is? Dat neemt niet weg dat je eerste verklaring waarom dat efficienter zou zijn, domweg niet waar is.
Sterker nog.. als ik ga optimaliseren is 'mijn' methode juist sneller, omdat er minder overhead inzit. Als je een goede programmeur één canvas geeft en de verantwoordelijkheid daar alles zelf op te tekenen, is dat per definitie efficienter dan 'jouw systeem'. Simpelweg omdat in de 'één-canvas-methode" minder overhead besloten zit.
Een simpel voorbeeld hiervan zijn events. Zodra je het op 'jouw manier' gaat doen, zal hij een hoop events moeten controleren of deze ook gezet zijn. Events die overbodig zijn binnen dit specifieke project, maar die vanwege de generieke aard vd. gebruikte componenten wel aanwezig zijn. Als je e.e.a. zelf maakt, zul je die events nooit toevoegen en raak je er dus ook geen cycles aan kwijt..
[ Voor 37% gewijzigd door Verwijderd op 22-11-2005 19:06 ]
mijn object is een afgeleide van een TPaintBox, en dat is op zijn beurt weer een afgeleide van een TGraphicControl. en als ik het goed heb dan roept de paint method alleen een onpaint event aan.LordLarry schreef op dinsdag 22 november 2005 @ 18:56:
Dus je wilt runtime een event zetten? Zie http://www.nldelphi.com/f...read.php?s=&threadid=2443
Maar je kan ook een TPanel of nog beter: een TGraphicControl nemen en de Paint method overriden.
wat dus bij mij niet gebeurt, of het gebeurd wel maar dan heb ik mijn onpaint niet goed gelinked. in ieder geval lukt het me niet om onpaint te tekenen. ik moet dus op de een of andere manier die onpaint van het object aan de gang krijgen.
en ik zal je link eens bekijken
ik heb even naar je link gekeken. en daar zie ik dat ze hetzelfde doen als tomatoman in zijn voorbeeld. namelijk een procedure schrijven en die toewijzen aan het object.
vb: obj.onpaint := blaat();
dit heb ik ook geprobeerd, maar blijkbaar doe ik iets fout. want de onpaint word helemaal niet aangeroepen bij mij. als iemand nog een idee heeft hoor ik het graag
[ Voor 47% gewijzigd door tombo_inc op 22-11-2005 21:30 ]
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Wat jij niet in je code doet is het OnPaint event koppelen aan de method MijnObjectPaint. Dat zal je ook nooit lukken, want een de method die OnPaint verwacht bevat parameters en die heeft jouw method niet. Zie de url die ik je gaf voor meer uitleg en een voorbeeld.
We adore chaos because we like to restore order - M.C. Escher
waarschijnlijk ben ik gewoon iets heel simpels vergeten, want volgens mijn logica
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
zoals ik al dacht was het gewoon een stomme fout. ik was namelijk wat parameters vergeten.
ff scheef gekeken dus. bedankt voor de hulp allemaal
ik heb nu de volgende werkende code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,unit2; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } blaat: TMijnObject; constructor Create(AOwner: TComponent); override; end; var Form1: TForm1; implementation {$R *.dfm} constructor TForm1.Create(AOwner: TComponent); begin inherited Create(AOwner); self.blaat := TMijnObject.create(AOwner); end; end. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,ExtCtrls; type TMijnObject = class(TPaintBox) constructor Create(AOwner: TComponent); override; procedure MijnObjectPaint(Sender: TObject); end; var MijnObject: TMijnObject; implementation uses unit1; constructor TMijnObject.Create(AOwner: TComponent); begin inherited create(AOwner); self.OnPaint := MijnObjectPaint; self.Parent := Form1; self.Left := 10; self.Top := 10; self.Width := 200; self.Height := 200; end; procedure TMijnObject.MijnObjectPaint(Sender: TObject); begin self.Canvas.MoveTo(20,20); self.Canvas.LineTo(100,100); end; end. |
ik heb alleen nu nog een vraag.
waarom moet je het object (blaat:TMijnObject) nog een parent toewijzen? want het is toch al een child van Form1. of mis ik nu weer iets.
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
We adore chaos because we like to restore order - M.C. Escher