Klasse aus einer Datei laden ?
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Ging i.d.H. darum an beliebiger Stelle im Programm ein Formular zu erzeugen ohne das direkt zu starten. Zusätzlich sollte mitprotokolliert werden welches Formular erzeugt wurde und wer es erzeugt hat, wie lange es geöffnet war und wann erzeugt and wann geschlossen.
Alle aktiven Formulare sollten in einem Dialog als Liste mit bestimmten Informationen über das Formular dargestellt werden. Das wäre so einfach mit Application.Forms nicht möglich gewesen.
Zusätzlich sollte der FormManager noch dafür sorgen, das nach einer bestimmten Zeit der Untätigkeit der User ausgeloggt wird und auch das protokolliert wird. Genau besehen war der FormManger das eigentliche mainprogram, MainForm war in dem Fall die Logform.
Hinterher stellte sich dann raus, das man auch einer Konsolenanwendung auf diese Weise bei Bedarf Formulare z.V. stellen konnte.
Entscheidend ist aber, das der FormManager nichts über das erzeugte Formular wissen mußte und es per Wurzelklasse trotzdem richtig erzeugt und bei Bedarf (FormShowAuto = true) direkt mit .Show darstellt.
Alle aktiven Formulare sollten in einem Dialog als Liste mit bestimmten Informationen über das Formular dargestellt werden. Das wäre so einfach mit Application.Forms nicht möglich gewesen.
Zusätzlich sollte der FormManager noch dafür sorgen, das nach einer bestimmten Zeit der Untätigkeit der User ausgeloggt wird und auch das protokolliert wird. Genau besehen war der FormManger das eigentliche mainprogram, MainForm war in dem Fall die Logform.
Hinterher stellte sich dann raus, das man auch einer Konsolenanwendung auf diese Weise bei Bedarf Formulare z.V. stellen konnte.
Entscheidend ist aber, das der FormManager nichts über das erzeugte Formular wissen mußte und es per Wurzelklasse trotzdem richtig erzeugt und bei Bedarf (FormShowAuto = true) direkt mit .Show darstellt.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
Deshalb:Wieso interessanter Style?...
Code: Alles auswählen
{-----------------------------------------------------------------------------
Class: TFormularManager
Method: DoAddNewForm
Author: root
Date: 05-Mai-2006
Arguments: AFormDesc: TFormDescriptor; AFormInfo: string
Result: TObject
-----------------------------------------------------------------------------}
function TFormularManager.DoAddNewForm(AFormDesc: TFormDescriptor;
AFormInfo: string): TObject;
const
cProcName = 'DoAddNewForm';
var
pShowMethod,
pShowModal : pointer;
begin
{ function body }
Result:= nil;
if (AFormDesc <> nil) then with AFormDesc do begin
try { create a new formular }
FormObj:= FormClass.Create(FormOwner);
FormName:= TComponent(FormObj).Name;
FFormList.Add(AFormDesc);
Result:= AFormDesc.FormObj;
if (AFormInfo = '')
then AFormDesc.FormInfo:= '['
+ FormName + ':'
+ FormObj.ClassName
+ ']';
if FormShowAuto then try
pShowMethod:= GetShowMethod(FormClass, FormObj);
if (pShowMethod <> nil) then TForm(FormObj).Show;
except
raise;
end; // of if FormShowAuto then try
except
raise;
end; // of try { create a new formular }
end; // of if (AFormDesc <> nil) then with AFormDesc do begin
end; // of TFormularManager.DoAddNewForm
MFG
Michael Springwald
Michael Springwald
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Ah, das meinst du.
cProcName ist für ein error reporting system vorgesehen. Tritt ein Fehler auf, kann der als log in eine Liste geschrieben werden und cProgName wird mit eingetragen. Das kann man noch um ModulName erweitern, dann könnte man das übers's Internet auch bei schweren Fehlern an den Service senden und die hätten sofort genaue Angaben wo der Fehler aufgetreten ist, welcher aufgetreten ist und wenn man's richtig chic macht auch noch die beteiligten Parameterwerte.
Das ist unter Delphi mit GExperts gemacht, das hat den Vorteil, das man Templates für die Methoden hat und solche Sachen wie der Header und cProcName aus dem Template automatisch erzeugt werden. Brauchste halt nur noch deine Verarbeitungslogik einbauen.
cProcName ist für ein error reporting system vorgesehen. Tritt ein Fehler auf, kann der als log in eine Liste geschrieben werden und cProgName wird mit eingetragen. Das kann man noch um ModulName erweitern, dann könnte man das übers's Internet auch bei schweren Fehlern an den Service senden und die hätten sofort genaue Angaben wo der Fehler aufgetreten ist, welcher aufgetreten ist und wenn man's richtig chic macht auch noch die beteiligten Parameterwerte.
Das ist unter Delphi mit GExperts gemacht, das hat den Vorteil, das man Templates für die Methoden hat und solche Sachen wie der Header und cProcName aus dem Template automatisch erzeugt werden. Brauchste halt nur noch deine Verarbeitungslogik einbauen.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
Code: Alles auswählen
könntest du dir eine Wurzel-Klasse für deine Objekte bauen.
MFG
Michael Springwald
Michael Springwald
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Der Wurzelklasse ist das "wurscht", du mußt halt eine Klasse TMyGraphicObj bauen und TRechteck davon ableiten.
Im Descriptor übergibst du dann TRechteck und erzeugst das Objekt. Dann wird immer der richtige Konstruktor aufgerufen. Da der alles in einer Liste hält, kannst du auch mehrere Objekte TRechteck erzeugen und die jeweils aktiven Visible setzen und die blitzartig austauschen. Dafür könnte man noch eine Mimik für die automatische Namensvergabe einbauen.
Daß das funzt sieht man am Quelltext oben (GetShowMethod). FormClass ist zwar als Wurzelklasse (TFormClass) vereinbart, enthält aber für dein Beispiel (müßte dann als TMyGraphicObj vereinbart werden) dann TRechteck und du darfst das auch so verwenden.
Also etwa:
AObjClass(AObjekt).
referenziert dann deine tatsächlichen Propertys und Methoden.
Im Descriptor übergibst du dann TRechteck und erzeugst das Objekt. Dann wird immer der richtige Konstruktor aufgerufen. Da der alles in einer Liste hält, kannst du auch mehrere Objekte TRechteck erzeugen und die jeweils aktiven Visible setzen und die blitzartig austauschen. Dafür könnte man noch eine Mimik für die automatische Namensvergabe einbauen.
Daß das funzt sieht man am Quelltext oben (GetShowMethod). FormClass ist zwar als Wurzelklasse (TFormClass) vereinbart, enthält aber für dein Beispiel (müßte dann als TMyGraphicObj vereinbart werden) dann TRechteck und du darfst das auch so verwenden.
Also etwa:
AObjClass(AObjekt).
referenziert dann deine tatsächlichen Propertys und Methoden.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
naja TRechteck ist von TPaint2bases objekt abgeleitet und das wieder rum von TPaint2Objekt das ist das ur objekt das ist von Tpesi.... abgeleitet damit das im OI angezeigt wird.... wie du siehst arbeite ich schon mit "Wurzelklassen" mit einer ganzen reihe wenn man so will*G*. Daher weiß ich nicht so recht wie ich anfangen soll/könnte.
MFG
Michael Springwald
Michael Springwald
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Also, TFormClass ist in der VCL so vereinbart:
TFormClass = class of TForm;
in deinem Fall folgt daraus:
TMyGraphObj = class of TPersistent. Du kannst aber der Einfachheit halber auch im Descriptor auch vereinbaren:
GraphObjClass: TPersistent; // ersetzt FormClass: TFormClass
Weil jedes Objekt in der Hierarchie das von TPersisten abgeleitet ist sich auch wieder TPersistent zuweisen läßt ist das erlaubt. Nun erzeugt man zuerst das Objekt:
GraphObj:= GraphObjClass.Create(ObjOwner);
dann gilt:
GraphObjClass(GraphObj)^.
referenziert das tatsächliche Objekt und man kann auf die Propertys und Methoden zugreifen. Da man aber keine Garantie hat, das eine bestimmte Methode oder Property auch wirklich da ist, benutzt man den Umweg über:
methodptr:= GraphObjClass(GraphObj)^.GetMethodAdress('MethodName');
oder:
propptr:= GraphObjClass(GraphObj)^.GetPropertyAdress('PropertyName');
Über einen Umweg kann man also auch mit den Strings etwas anfangen. Da wird sich also auch noch ein Trick finden, wie man per String die richtige Klasse erzeugt.
TFormClass = class of TForm;
in deinem Fall folgt daraus:
TMyGraphObj = class of TPersistent. Du kannst aber der Einfachheit halber auch im Descriptor auch vereinbaren:
GraphObjClass: TPersistent; // ersetzt FormClass: TFormClass
Weil jedes Objekt in der Hierarchie das von TPersisten abgeleitet ist sich auch wieder TPersistent zuweisen läßt ist das erlaubt. Nun erzeugt man zuerst das Objekt:
GraphObj:= GraphObjClass.Create(ObjOwner);
dann gilt:
GraphObjClass(GraphObj)^.
referenziert das tatsächliche Objekt und man kann auf die Propertys und Methoden zugreifen. Da man aber keine Garantie hat, das eine bestimmte Methode oder Property auch wirklich da ist, benutzt man den Umweg über:
methodptr:= GraphObjClass(GraphObj)^.GetMethodAdress('MethodName');
oder:
propptr:= GraphObjClass(GraphObj)^.GetPropertyAdress('PropertyName');
Über einen Umweg kann man also auch mit den Strings etwas anfangen. Da wird sich also auch noch ein Trick finden, wie man per String die richtige Klasse erzeugt.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Nun gehen wir das mal von der anderen Seite an. Du rufts in deinem Formular irgendwo die Objekt-Daten aus einer Datei ab. Anstatt aber nun im Formular Objekt-Variablen in der Form FRechteck: TRechteck zu deklarieren benutzt du einfach einen Platzhalter, also etwa FRechteck: TObject. Du baust dir jetzt in einen solchen Manager in abgespeckter Form für deine graphischen Objekte. Deinem Formular sind die Classen per uses bekannt. Wenn jetzt dein Type-String aus der Datei angewackelt kommt brauchst du nur noch eine Funktion der Form:
Das Ergebnis weist du dem entsprechenden Descriptor-Feld zu und läßt dir das Objekt erzeugen und weist es dem Zielobjekt zu:
FRechteck:= DoAddGraphObj(AGraphDesc);
Noch einfacher ginge das nur noch mit einem Set:
TMyGraphObjectsSet = (my_rechteck, my_kreis, my_oval...);
TMyGraphObject = my_rechteck..my_oval;
Jetzt könnte man in der Datei (Stream, ganz egal) das erste Feld mit einer Variablen vom Typ = TMyGraphObject speichern und per case ... of die Klasse ermitteln. Genau betrachtet brauchst du nicht mal Objektvariable nehmen, sondern könntest einfach die Dinger in einer TList speichern und hättest trotzdem volle Kontrolle. Halt immer auf einem kleinen Umweg aber äußerst elegant.
Code: Alles auswählen
function TMyForm.GetMyClassType(AClassType: string): TPersistent;
begin
Result:= nil;
if (LowerCase(AClassType) = 'trechteck') then Result:= TRechteck
else if (LowerCase(AClassType) = 'tsonstwas') then Result:= TSonstwas;
end;
FRechteck:= DoAddGraphObj(AGraphDesc);
Noch einfacher ginge das nur noch mit einem Set:
TMyGraphObjectsSet = (my_rechteck, my_kreis, my_oval...);
TMyGraphObject = my_rechteck..my_oval;
Jetzt könnte man in der Datei (Stream, ganz egal) das erste Feld mit einer Variablen vom Typ = TMyGraphObject speichern und per case ... of die Klasse ermitteln. Genau betrachtet brauchst du nicht mal Objektvariable nehmen, sondern könntest einfach die Dinger in einer TList speichern und hättest trotzdem volle Kontrolle. Halt immer auf einem kleinen Umweg aber äußerst elegant.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
nein schnullerbacke genau so wollte ich es nicht mehr haben.
Denn diesen weg bin ich am Anfang auch gegangen nur war mein weg noch einfach !
Aber ich sehe schon es geht nicht anders. Ich werde wohl leider diesen weg wieder einschlagen müssen. Schade !
ich hatte das in etwa so:
und genau diesen weg wollte ich jetzt nicht mehr gehen. Da die Daten jetzt in einer Datei drin stehen wollte ich so einen weg gehen:
wobei jetzt paint2Obj mit dem wert der in ObjTyp drin steht installisiert werden soll z.b. mit
TRechteck oder TKrei oder oder oder !
so ich hoffe jetzt ist klar wie ich es haben möchte !
genau so soll es irgenwie gehen !
ich möchte nur noch ein neues objekt irgenwo einfügen und dann diese Datei ändern und fertig !
mehr möchte ich garnicht mehr machen müssen.
sonst hätte ich mir die Datei sparen können
die Datei sieht übrings so aus:
die sektionen wie [Allgemein] werden jetzt als werden jetzt zu einer seite hinzugefügt
dise seite bekommt die einträge der jeweiligen sektion als SpeedButton hinzugefügt.
So Dachte ich mir das aufjedenfall und als Hint der jeweilinge speedbuttons wird einfach
der angeben Key genommen z.b. TKreis und der Wert nach dem = zeichen
das ist das entsprechende Icon was angezeigt wird.
Denn diesen weg bin ich am Anfang auch gegangen nur war mein weg noch einfach !
Aber ich sehe schon es geht nicht anders. Ich werde wohl leider diesen weg wieder einschlagen müssen. Schade !
ich hatte das in etwa so:
Code: Alles auswählen
procedure addobj(toolindex:Integer)
var
paint2Obj:TPaint2Obj;
begin
if toolindex = 0 then paint2obj:=TRechteck.Create
if toolindex = 1 then paint2obj:=TKreis.Create
// und dann kamen sie in einer liste:
objlist.add(paint2obj);
// vohrer wurden noch eine std. größe gesetzt von 20 mal 20 Pixel sowie eine std. hintergrund Farbe
end;
Code: Alles auswählen
procedure addObj(objTyp:String);
var
paint2Obj:TPaint2obj
begin
paint2obj:=objTyp.CREATE
end;
TRechteck oder TKrei oder oder oder !
so ich hoffe jetzt ist klar wie ich es haben möchte !
genau so soll es irgenwie gehen !
ich möchte nur noch ein neues objekt irgenwo einfügen und dann diese Datei ändern und fertig !
mehr möchte ich garnicht mehr machen müssen.
sonst hätte ich mir die Datei sparen können

die Datei sieht übrings so aus:
Code: Alles auswählen
[Standart(alt)]
TRectEck=STD_TViereck
TKreis=STD_TKreis
TLine=STD_TLine
TText=STD_Text
troundrec=STD_TAbgerundetes
[Standart(neu)]
TRoationRectEck=STD2_TVIERECK
[Allgemein]
[Extras]
ImageListB=imagelistB.bmp
dise seite bekommt die einträge der jeweiligen sektion als SpeedButton hinzugefügt.
So Dachte ich mir das aufjedenfall und als Hint der jeweilinge speedbuttons wird einfach
der angeben Key genommen z.b. TKreis und der Wert nach dem = zeichen
das ist das entsprechende Icon was angezeigt wird.
MFG
Michael Springwald
Michael Springwald
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Dann mach die Laderoutine in den Manager und übergeb den Dateinamen. Die Liste legt er ja offen, also kannst du die zum anzeigen benutzen. In dem Fall muß der Manager deine Objekte kennen und dem Formular können die "wurscht" sein.
Gibt ja schließlich immer mehrere Wege zum Ziel.
Gibt ja schließlich immer mehrere Wege zum Ziel.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
Ja, @pluto. Aber eher mit Klassen-Templates. Die eigentliche Produktion der Klasse erfolgt über ein Template (TFormClass). Dein Problem ist ja nur aus einem String die richtige Klasse zu erzeugen. Der kleine Umweg über eine Funktion die den Template-Typ ermittelt ist ja nun einfach zu gehen.
Aber das ändert nix daran, das einige Methoden verwendet werden, die den Verdacht nahelegen, das man mit etwas Suchen auch eine Möglichkeit findet über die VMT auch die richtige Klasse per String zu produzieren. Ein bißchen was mußt du auch schon selbst machen. Das Beispiel zeigt ja schon einen halbwegs gangbaren Weg der Trennung von Quellcode und ausführbaren Formular. Schließlich machen das Delphi/Lazarus ja auch, indem sie die Formulardatei aufrufen. Das wäre schonmal ein Ansatzpunkt zum suchen.
Aber das ändert nix daran, das einige Methoden verwendet werden, die den Verdacht nahelegen, das man mit etwas Suchen auch eine Möglichkeit findet über die VMT auch die richtige Klasse per String zu produzieren. Ein bißchen was mußt du auch schon selbst machen. Das Beispiel zeigt ja schon einen halbwegs gangbaren Weg der Trennung von Quellcode und ausführbaren Formular. Schließlich machen das Delphi/Lazarus ja auch, indem sie die Formulardatei aufrufen. Das wäre schonmal ein Ansatzpunkt zum suchen.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
Ich seh dein Problem nicht.pluto hat geschrieben: wobei jetzt paint2Obj mit dem wert der in ObjTyp drin steht installisiert werden soll z.b. mit
TRechteck oder TKrei oder oder oder !
Warum machst du denn nicht einfach ne Funktion wie ich sie z.B. in opbitmapformats gemacht habe? Hier hole ich die Klasse ab, welche einen Mime Typen lesen kann z.B. 'image/png':
Code: Alles auswählen
type TOpBmpClass = class of TCanvasOPBitmap;
function GetClassFromMime(MimeType: string): TOpBmpClass;
begin
Result := nil;
if MimeType = MIME_JPG then Result := TJPEGImage else
if MimeType = MIME_PNG then Result := TPNGImage else
if MimeType = MIME_GIF then Result := TGIFImage else
if MimeType = MIME_BMP then Result := TBMPImage else
if MimeType = MIME_TIF then Result := TTIFFImage else
etc....
end;
Code: Alles auswählen
procedure TOPPicture.LoadFromStream(Stream: TStream; Mime: string);
var cls: TOpBmpClass;
begin
cls := GetClassFromMime(Mime);
if cls <> nil then
begin
fCurrentFormat := cls.create;
(fCurrentFormat as cls).LoadFromStream(Stream);
end else raise EPasBitMapError.Create('Stream Format not supported');
end;
Was ist den daran nicht In Ordung? Oder ist dein Problem ein Anderes?
-
- Beiträge: 1187
- Registriert: Mi 13. Dez 2006, 10:58
- OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
- CPU-Target: AMD A4-6400 APU
- Wohnort: Hamburg
@theo
@Pluto möchte eher sowas haben, wenn ich das richtig verstanden hab:
also den String direkt benutzen und CreateObject muß den richtigen Typ selbst einsetzen.
@Pluto möchte eher sowas haben, wenn ich das richtig verstanden hab:
Code: Alles auswählen
function LoadObject(filename: string): TObject;
var
f: Text;
typePart,
s: string;
begin
// file öffnen
s:= readln(f);
// ab hier parsen
Result:= CreateObject(typePart);
end;
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
Aber das macht doch LoadFromStream(Stream: TStream; Mime: string); auch, den richtigen Typ selbst setzen. Wo ist der Unterschied?schnullerbacke hat geschrieben:@theo
@Pluto möchte eher sowas haben, wenn ich das richtig verstanden hab:
...
also den String direkt benutzen und CreateObject muß den richtigen Typ selbst einsetzen.