Dynamisches Array mit PropertyEditor in eigener Komponente
-
- Beiträge: 608
- Registriert: Di 19. Mai 2015, 20:05
- OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
- CPU-Target: x86_64-linux-gtk2
Dynamisches Array mit PropertyEditor in eigener Komponente
Hallo zusammen,
ich brauche mal wieder Unterstützung. Ich habe eine kleine Komponente mit einem Propertyeditor erzeugt. Im Propertyeditor kann ich ein Polygon zeichnen und dieses wird in die Komponente übernommen. Lieder schaffe ich es nicht das Array in die lfm zu streamen. Ich weiß auch nicht ob das überhaupt möglich ist. Momentan streame ich das Array in eine Datei und lade die dann wieder. Bei der Variante habe ich aber das Problem das ich zur Designzeit noch nicht weiß welchen Pfad ich verwenden kann. Um mir zu behelfen speichere ich momentan in das Homeverzeichnis. Das kann aber nicht der richtige Weg sein.
Falls jemand weiß wie man das macht würde ich mich über eine Antwort sehr freuen. Ich hänge mal meine kleine Komponente an. Sie wird unter misc angezeigt.
Viele Grüße
Bernd
ich brauche mal wieder Unterstützung. Ich habe eine kleine Komponente mit einem Propertyeditor erzeugt. Im Propertyeditor kann ich ein Polygon zeichnen und dieses wird in die Komponente übernommen. Lieder schaffe ich es nicht das Array in die lfm zu streamen. Ich weiß auch nicht ob das überhaupt möglich ist. Momentan streame ich das Array in eine Datei und lade die dann wieder. Bei der Variante habe ich aber das Problem das ich zur Designzeit noch nicht weiß welchen Pfad ich verwenden kann. Um mir zu behelfen speichere ich momentan in das Homeverzeichnis. Das kann aber nicht der richtige Weg sein.
Falls jemand weiß wie man das macht würde ich mich über eine Antwort sehr freuen. Ich hänge mal meine kleine Komponente an. Sie wird unter misc angezeigt.
Viele Grüße
Bernd
- Dateianhänge
-
TCustomShape.zip
- (113.39 KiB) 69-mal heruntergeladen
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Ohne mich tiefer damit zu befassen oder den Sinn deines Vorhabens verstehen zu wollen:
Hast du das gelesen?
https://wiki.lazarus.freepascal.org/Str ... ponents/de
Zum "üblichen" Speicherort für die Konfiguration:
https://lazarus-ccr.sourceforge.io/docs ... igdir.html
Hast du das gelesen?
https://wiki.lazarus.freepascal.org/Str ... ponents/de
Zum "üblichen" Speicherort für die Konfiguration:
https://lazarus-ccr.sourceforge.io/docs ... igdir.html
- Winni
- Beiträge: 1577
- Registriert: Mo 2. Mär 2009, 16:45
- OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
- CPU-Target: 64Bit
- Wohnort: Fast Dänemark
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Hi!
Zu dem Speichern eines Arrays in der lfm Datei:
Ich habe noch nie ein Array in einer lfm gesehen.
Aber die Lösung ist einfach:
Es gibt diverse Komponenten, dessen Werte in einer Stringlist gespeichert werden.
Also Deine Koordinaten als string in X,Y in einer Stringlist speichern
Winni
Zu dem Speichern eines Arrays in der lfm Datei:
Ich habe noch nie ein Array in einer lfm gesehen.
Aber die Lösung ist einfach:
Es gibt diverse Komponenten, dessen Werte in einer Stringlist gespeichert werden.
Code: Alles auswählen
Items.Strings = (
'Emboss'
'Cascade'
'Sandwich'
...
)
Also Deine Koordinaten als string in X,Y in einer Stringlist speichern
Winni
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Das sieht mir etwas verunglückt aus. Denn dein Property-Editor ist für die Ausgabe und die Maus-Interaktion zuständig. Das ist schlecht, denn was machst deine Shape-Komponente zur normalen Laufzeit, wenn der Property-Editor gar nicht zur Verfügung steht?
Als erstes: warum ist TMyShape ein TObject und kein TGraphicControl oder TWinControl? Diese können sich selbst zeichnen und kennen die Maus, so dass du diesen Code in die Komponente verlagern kannst. Als Konsequenz kann sich dann TMyShape selbst um die Ausgabe auf seinem/ihrem eigenen Canvas und das Hinzufügen von Punkten kümmern. TMyShape hat wahrscheinlich eine Array-ähnliche Eigenschaft "Points" (oder so ähnlich) für die Polygon-Punkte. Zuständig für den Streaming-Mechanismus neuer Property-Typen ist die Methode DefineProperties, die die Schreib- und Lese-Methoden definiert, wie die Property-Daten in der lfm-Datei geschrieben/gelesen werden. Das Verfahren wird z.B. in dem Example-Projekt "widestringstreaming" durchgespielt, sowie in diversen LCL-Komponenten (TGridColumnTitle in grids). Auch TStrings hat ein DefineProperties. Da es wenig Sinn macht, die Punkt-Koordinaten binär in die lfm-Datei zuschreiben, müssen die von DefineProperties festgelegten Schreib-/Lesemethoden die Koordinaten in Strings umwandeln.
Ohne jetzt viel ausprobiert zu haben, hier die grobe Struktur:
Wenn du dann später die Punkte zur Designzeit eingeben willst, brauchst du einen PropertyEditor für TMyshapePoints (ich sehe gerade: möglicherweise muss die Eigenschaft Points als TMyShapePoints deklariert sein...). Ähnlich wie du es schon gemacht hast, erzeugt der ein eigenes Formular, in dem eine Instanz von TMyShape kreiert wird. Mit den Methoden von TMyShape wird diese Instanz gezeichnet, und mit den Mausmethoden werden neue Punkte hinzugefügt usw. Das ganze ist relativ kompliziert und wenig dokumentiert... Am besten du siehst dir die bereits bestehenden PropertyEditoren in der Unit PropEdits an
[EDIT]
Eine andere Variante fällt mir noch ein, nämlich die Punkte nicht in einem dynamischen Array sondern in einer Collection zu speichern. TCollection und Nachfahren sind sowas wie TList oder meinetwegen auch ein Array, aber halt integriert in die IDE. Die einzige Grundbedingung ist, dass die Collection-Items Nachfahren von TCollectionItem sein müssen, das heißt du musst eine Wrapper-Klasse für TPoint schreiben. Aber ansonsten ist alles fertig, auch die Integration in den Objekt-Inspektor. DefineProperties ist nicht mehr nötig.
Als erstes: warum ist TMyShape ein TObject und kein TGraphicControl oder TWinControl? Diese können sich selbst zeichnen und kennen die Maus, so dass du diesen Code in die Komponente verlagern kannst. Als Konsequenz kann sich dann TMyShape selbst um die Ausgabe auf seinem/ihrem eigenen Canvas und das Hinzufügen von Punkten kümmern. TMyShape hat wahrscheinlich eine Array-ähnliche Eigenschaft "Points" (oder so ähnlich) für die Polygon-Punkte. Zuständig für den Streaming-Mechanismus neuer Property-Typen ist die Methode DefineProperties, die die Schreib- und Lese-Methoden definiert, wie die Property-Daten in der lfm-Datei geschrieben/gelesen werden. Das Verfahren wird z.B. in dem Example-Projekt "widestringstreaming" durchgespielt, sowie in diversen LCL-Komponenten (TGridColumnTitle in grids). Auch TStrings hat ein DefineProperties. Da es wenig Sinn macht, die Punkt-Koordinaten binär in die lfm-Datei zuschreiben, müssen die von DefineProperties festgelegten Schreib-/Lesemethoden die Koordinaten in Strings umwandeln.
Ohne jetzt viel ausprobiert zu haben, hier die grobe Struktur:
Code: Alles auswählen
type
TMyshapePoints = array of TPoint;
TMyShape = class(TGraphicControl)
private
FPoints: TMyShapePoints;
procedure ReadPoints(Filer: TFiler);
procedure Writepoints(Filer: TFiler);
function GetPoint(AIndex: Integer): TPoint;
procedure Setpoint(AIndex: Integer; AValue: TPoint);
protected
procedure DefineProperties(Filer: TFiler); override;
procedure Paint; override;
procedure MouseDown(...); override;
procedure MouseMove(...); override;
procedure MouseUp(...); override;
...
published
property Points[Index: Integer]: TPoint read GetPoint write Setpoint;
end;
procedure TMyShape.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineProperty('Points', @ReadPoints, @WritePoints, true);
end;
procedure TMyShape.WritePoints(Writer: TWriter);
var
L: TStrings;
begin
L := TStringList.Create;
try
for i := 0 to High(FPoints) do
L.Add('%d,%d', [FPoints[i].X, FPoints[i].Y]);
L.WriteData(Writer);
finally
L.Free;
end;
end;
procedure TMyshape.ReadPoints(Reader: TReader);
var
L: TStrings;
sa: TStringArray;
begin
L := TStringList.Create;
try
L.ReadData(Reader);
Setlength(FPoints, L.Count);
for i := 0 to L.Count-1 do
begin
sa := L[i].Split(',');
FPoints[i].X := StrToInt(sa[0]);
FPoints[i].Y := StrToInt(sa[1]);
end;
finally
L.Free;
end;
end;
function TMyShape.GetPoint(AIndex: Integer): TPoint;
begin
Result := FPoints[AIndex];
end;
procedure TMyShape.Setpoint(AIndex: Integer; AValue: TPoint);
begin
FPoints[AIndex] := AValue;
end;
[EDIT]
Eine andere Variante fällt mir noch ein, nämlich die Punkte nicht in einem dynamischen Array sondern in einer Collection zu speichern. TCollection und Nachfahren sind sowas wie TList oder meinetwegen auch ein Array, aber halt integriert in die IDE. Die einzige Grundbedingung ist, dass die Collection-Items Nachfahren von TCollectionItem sein müssen, das heißt du musst eine Wrapper-Klasse für TPoint schreiben. Aber ansonsten ist alles fertig, auch die Integration in den Objekt-Inspektor. DefineProperties ist nicht mehr nötig.
Code: Alles auswählen
type
TPointCollectionItem = class(TCollectionItem)
private
FPoint: TPoint;
function GetX: Integer;
function GetY: Integer;
procedure SetX(Value: Integer);
procedure SetY(Value: Integer);
public
procedure Assign(ASource: TPersistent); override;
published
property Point: TPoint read FPoint write FPoint;
property X: Integer read GetX write SetX;
property Y: Integer read GetY write SetY;
end;
TPointCollection = class(TCollection)
private
function GetItem(AIndex: Integer): TPointCollectionItem;
procedure SetItem(AIndex: Integer; AValue: TPointCollectionItem);
public
constructor Create;
property Items[AIndex: Integer]: TPointCollectionItem read Getitem write SetItem;
end;
procedure TPointCollectionItem.Assign(ASource: TPersistent);
begin
if ASource is TPointCollectionItem then
begin
FPoint := ASource.Point;
inherited Assign(ASource);
end else
raise Exception.Create('Assign funktioniert nur für TPointCollectionItem');
end;
function TPointCollectionItem.GetX: Integer;
begin
Result := FPoint.X;
end;
procedure TPointCollectionItem.SetX(Value: Integer);
begin
FPoint.X := Value;
end;
constructor TPointCollection.Create;
begin
inherited Create(TPointCollectionItem);
end;
function TPointCollection.GetItem(AIndex: Integer): TPointCollectionItem;
begin
Result := TPointCollectionItem(inherited GetItem(AIndex));
end;
procedure TPointCollection.SetItem(AIndex: Integer; AValue: TPointCollectionItem);
begin
inherited SetItem(AIndex, AValue);
end;
Zuletzt geändert von wp_xyz am Sa 11. Jun 2022, 13:17, insgesamt 1-mal geändert.
-
- Beiträge: 608
- Registriert: Di 19. Mai 2015, 20:05
- OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
- CPU-Target: x86_64-linux-gtk2
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Vielen Dank für eure Antworten!
@ Theo: Die Links sind sehr interessant für mich. Habe ich leider selber nicht gefunden (war wohl mal wieder zu blöd zum Googeln
).
@ Winni: Die Werte als Strings zu speichern ist auf jeden Fall eine Option. Daran hab ich noch gar nicht gedacht.
@ wp_xyz: Das ist beeindruckend wie du so eine Antwort aus dem Ärmel schüttelst. Ich werde deine Vorschläge bestimmmt alle der Reihe nach probieren. Mal schauen wie weit ich komme.
Nur noch zum besseren Verständniss. Eigentlich werkel ich an einem Panel herum. Geht auch schon einiges wie ich will. Jetzt kam mir der Gedanke das ich dem Panel zur Designtime eine eigene Form geben kann. Darüber ob ich die dann zur Laufzeit nochmal ändern möchte habe ich ehrlich gesagt noch nicht nach gedacht. Über den Sinn kann man da natürlich streiten, aber ich mach es ja zum Spaß und es hat mich halt gereizt. Die hier gepostete kleine Komponente nutze ich eigentlich nur um Verschiedenes zu testen. Aber eventuell wird ja noch mehr daraus. Jedenfalls hab ich nun reichlich zum Nachdenken!
Viele Grüße
Bernd
@ Theo: Die Links sind sehr interessant für mich. Habe ich leider selber nicht gefunden (war wohl mal wieder zu blöd zum Googeln

@ Winni: Die Werte als Strings zu speichern ist auf jeden Fall eine Option. Daran hab ich noch gar nicht gedacht.
@ wp_xyz: Das ist beeindruckend wie du so eine Antwort aus dem Ärmel schüttelst. Ich werde deine Vorschläge bestimmmt alle der Reihe nach probieren. Mal schauen wie weit ich komme.
Nur noch zum besseren Verständniss. Eigentlich werkel ich an einem Panel herum. Geht auch schon einiges wie ich will. Jetzt kam mir der Gedanke das ich dem Panel zur Designtime eine eigene Form geben kann. Darüber ob ich die dann zur Laufzeit nochmal ändern möchte habe ich ehrlich gesagt noch nicht nach gedacht. Über den Sinn kann man da natürlich streiten, aber ich mach es ja zum Spaß und es hat mich halt gereizt. Die hier gepostete kleine Komponente nutze ich eigentlich nur um Verschiedenes zu testen. Aber eventuell wird ja noch mehr daraus. Jedenfalls hab ich nun reichlich zum Nachdenken!
Viele Grüße
Bernd
-
- Beiträge: 608
- Registriert: Di 19. Mai 2015, 20:05
- OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
- CPU-Target: x86_64-linux-gtk2
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Hallo zusammen,
ich denke ich habe nun (nach etlichen Versuchen) eine Variante gefunden die das macht was ich gerne möchte. Jedenfalls wird das Array (als Strings) problemlos in die lfm geschrieben und gelesen. Ist zwar sicherlich etwas speziell, aber vielleicht interessiert es ja doch irgend jemanden. Der angehängte Test wird unter Misc angezeigt.
Ein weiteres Problemchen konnte ich leider noch nicht lösen. Ich möchte das durch doppelklick auf die Komponente der Editor aufgeht. Ich dachte eigentlich das normalerweise automatisch ein Doppelklick in der Edit Methode des PropertyEditors landet. Ist wohl leider nicht so. Ich habe dann auch probiert, durch überschreiben von GetVerbCount, GetVerb und ExecuteVerb im Kontextmenü einen Menüeintrag hinzuzufügen. Das funktioniert leider auch nicht. Bei einem KomponentenEditor hab ich es schon problemlos hinbekommen. Weiß vielleicht jemand wo da der Unterschied liegt bzw. was ich machen muss?
Viele Grüße
Bernd
ich denke ich habe nun (nach etlichen Versuchen) eine Variante gefunden die das macht was ich gerne möchte. Jedenfalls wird das Array (als Strings) problemlos in die lfm geschrieben und gelesen. Ist zwar sicherlich etwas speziell, aber vielleicht interessiert es ja doch irgend jemanden. Der angehängte Test wird unter Misc angezeigt.
Ein weiteres Problemchen konnte ich leider noch nicht lösen. Ich möchte das durch doppelklick auf die Komponente der Editor aufgeht. Ich dachte eigentlich das normalerweise automatisch ein Doppelklick in der Edit Methode des PropertyEditors landet. Ist wohl leider nicht so. Ich habe dann auch probiert, durch überschreiben von GetVerbCount, GetVerb und ExecuteVerb im Kontextmenü einen Menüeintrag hinzuzufügen. Das funktioniert leider auch nicht. Bei einem KomponentenEditor hab ich es schon problemlos hinbekommen. Weiß vielleicht jemand wo da der Unterschied liegt bzw. was ich machen muss?
Viele Grüße
Bernd
- Dateianhänge
-
TCustomShape2.zip
- (63.04 KiB) 57-mal heruntergeladen
Zuletzt geändert von wennerer am Fr 26. Aug 2022, 15:02, insgesamt 1-mal geändert.
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Für das Verhalten bei einem Doppelklick auf der Komponente ist nicht der PropertyEditor, sondern der Komponenten-Editor zuständig: https://wiki.freepascal.org/How_To_Writ ... nt_editors. Das Grundgerüst dafür (mit integrierter Kurz-Doku) und all die Beispiele aus der LCL findest du in der Datei componenteditors.pas im Verzeichnis components/ideintf der Lazarus-Installation.wennerer hat geschrieben: So 21. Aug 2022, 20:43 Ich möchte das durch doppelklick auf die Komponente der Editor aufgeht.
Zuletzt geändert von wp_xyz am Mo 22. Aug 2022, 17:08, insgesamt 1-mal geändert.
-
- Beiträge: 608
- Registriert: Di 19. Mai 2015, 20:05
- OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
- CPU-Target: x86_64-linux-gtk2
Re: Dynamisches Array mit PropertyEditor in eigener Komponente
Hallo,
wp_xyz schrieb:
Jedenfalls mal wieder vielen Dank für deine Hilfe!
Viele Grüße
Bernd
wp_xyz schrieb:
Okay, wenn man drüber nachdenkt macht das ja auch Sinn. Ein PropertyEditor ist ja nur für eine Eigenschaft zuständig. Ich hab mich da wahrscheinlich blenden lassen weil es im PropertyEditor auch die Methoden GetVerbCount, GetVerb und ExecuteVerb gibt die beim KomponentenEditor für das Kontextmenü überschrieben werden.Für das Verhalten bei einem Doppelklick auf der Komponente ist nicht der PropertyEditor, sondern der Komponenten-Editor zuständig
Jedenfalls mal wieder vielen Dank für deine Hilfe!
Viele Grüße
Bernd