![]() |
konzeptionelle Gedanken optische Darstellung Stammbaum
Ein herzliches Hallo an alle,
Ich würde gern einen Stammbaum grafisch und interaktiv ausgeben. Jedes Element soll anklickbar sein. Ich dachte mir, dass ich dazu zwei Klassen brauche: TFamilyTree (abgeleitet von TCanvas) und TTreeItem. TFamilyTree hat folgende Eigenschaften, da ich bis zu 4 Generationen (optional) abbilden möchte:
TTreeItem hat folgende Eigenschaften:
Nun soll TFamilyTree beispielsweise nie größer als 800x600 Pixel sein. Die Größe des Offsets soll sich dynamisch generieren. Dazu muss folgendes gecheckt werden:
Hat ein Proband nun sehr viele Kinder, wir das Offset schnell breiter, als 800 Pixel. Mich würde nun interessieren, wie man möglichst sinnvoll und WIE die Breite und Höhe des Offsets vorab berechnen kann und wie man bei einer festen Breite und Höhe von TFamilyTree diesen via Scrollbars scrollen kann, aber so, dass man die Objekte immer noch richtig anklicken kann (also eine Koordinatenfrage!). Hat jemand etwas ähnliches schon einmal versucht? Könnt ihr mir folgen? Habe ich mir die richtigen Gedanken gemacht? Danke Euch im Voraus. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Meines Erachtens ist der Ansatz grundlegend falsch. Du vermengst hier die Datenhaltung und die Bildschirmdarstellung.
Die Daten selbst sollten völlig I/O-unabhängig gespeichert werden, und für die Darstellung eines Knotens am Bildschirm definierst du ein TFrame. Die Frames erzeugst du dynamisch nach Bedarf. Anklickbar sind die ganz von selbst, wenn sie einen scrollbaren Container als Parent haben (die Form selbst oder eine Scrollbox auf der Form), da brauchst du nichts selbst zu programmieren. Übrigens: Was du mit
Delphi-Quellcode:
vorhast, versteh ich überhaupt nicht.
offset: TBitmap
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Ich denke auch, dass du da den falschen Ansatz wählst.
Wenn überhaupt, wird ein Teilbereiches des Baumes auf einen Canvas gemalt (ggf. über eine dritte Klasse), aber sollte sich nicht selber zeichnen, indem es von Canvas abgeleitet ist. Warum eigentlich diese Beschränkung auf 800 x 600 Pixel in der Ausgabe? Um mir Ideen zu holen, würde ich immer bei den großen Playern schauen, wie die bestimmte Sachen lösen. Kennst du myheritage.com? Deren Clientsoftware ist zwar etwas altbacken, aber die Darstellung eines Stammbaumes in der Web-Darstellung finde ich sehr gelungen. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Zitat:
Natürlich kenne ich MyHeritage :wink: Von welcher dritten Klasse sprichst Du? Die Beschränkung war ein Beispiel. Es get nur darum, dass das ganze Scrollbar sein soll. Ich bin für Input weiterhin sehr dankbar, weil es zum Beispiel auch darum gehen wird, wie man dann die einzelnen Verbindungsknoten unter den Einträgen zeichnet etc. Äußerst komplex. :( |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Ich würde TTreeItem (oder besser TPerson) so definieren:
Delphi-Quellcode:
Und mehr brauchst du gar nicht. Die Kinder musst du gar nicht speichern, weil sie sich implizit ergeben: Das sind die Personen, wo entweder Father oder Mother das jeweilige Subjekt sind.
TPerson = class
Mother: TPerson; Father: TPerson; Name: String; end; Dann noch TFamilyTree in etwa so:
Delphi-Quellcode:
Um den Baum (eigentlich ein Graph) zu zeichnen, musst du über den Graphen iterieren und ihn in „Schichten“ (= Generationen) anordnen.
TFamilyTree = class
Family: TList<TPerson>; function TreeToLayers(Subject: TPerson; Depth: Integer): TList<TLayer>; end;
Delphi-Quellcode:
In dem Beispielcode werden nur die Ahnen berücksichtigt, aber es ist nicht schwer es so zu erweitern, dass auch Kinder, Geschwichter, Cousins etc. in die jeweiligen Schichten eingefügt werden. Man muss dann nur aufpassen, dass man einen Knoten nicht mehrfach hinzufügt.
type
TLayer = TList<TPerson>; function TFamilyTree.TreeToLayers(Subject: TPerson; Depth: Integer): TList<TLayer>; procedure AssignPerson(Person: TPerson; Layers: TList<TLayer>; LayerIndex: Integer); begin if LayerIndex >= Layers.Count then exit; Layers[CurrentLayerIndex].Add(Person); AssignPerson(Person.Mother, Layers, LayerIndex+1); AssignPerson(Person.Father, Layers, LayerIndex+1); end; var i: Integer; begin Result := TList<TLayer>.Create; for i := 0 to Depth-1 do Result.Add(TLayer.Create); AssignPerson(Subject, 0); end; Die Schichten kannst du dann problemlos in einer Richtung zeichnen. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
In einem meiner kommerziellen Programme habe ich etwas vergleichbares für Schach, speziell Eröffnungsvarianten, gebaut.
![]() Dafür habe ich das TdxOrgChart von DexExpress verwendet. Diese Komponente bietet eigentlich alles, was Du für Deine Anforderung benötigst und natürlich gibt es auch eine datenbasierte Variante. Nachteil ist aber natürlich der nicht unerhebliche Kostenfaktor, da es das TdxOrgChart nur als Teil der VCL Subscription gibt. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Für die Darstellung der einzelnen Knoten nimmst du geeignete visuelle Komponenten. Die Komponenten wissen auch, wenn sie gescrollt werden, wann sie angeklickt werden, und du kannst in den entsprechenden Ereignisroutinen (onclick etc.) darauf reagieren. Um das Scrollen brauchst du dich nicht zu kümmern. Die Bildschirmdarstellung sollte ausser der Bildschirmdarstellung nichts tun, insbesonders keine Strukturdaten enthalten. Auf Grund der Strukturdaten, die du in eigenen Klassen bereit hältst, die mit der Darstellung am Schirm gar nichts zu tun haben, erstellst du dann die visuellen Komponenten. Also nicht: Visuelle Komponente enthält Referenz auf Datenbankwert sondern - Aus der Datenbank erzeuge Baum bzw. Netzstruktur, und dann erzeuge für jeden Netzknoten adäquate visuelle Controls mit geeigneten Namen. Wenn ein Control angeklickt wurde, weisst du auf Grund des Parameters "Sender" in allen Ereignisbehandlungsroutinen, wer angeklickt wurde. Zitat:
Person -> Vater Person -> Mutter Person -> nächstälteres Geschwister Person -> nächstjüngeres Geschwister Person -> jüngstes (oder ältestes) Kind edit Wobei das mit den Geschwistern komplizierter ist, es gibt ja auch Halbgeschwister und dergleichen. Da ist es vielleicht wirklich am besten, wie Namenloser schreibt, nur die Eltern zu speichern und für alles andere die Liste aller Personen durchzugehen. So viele Leute können das nicht sein, dass da Performance Probleme drohen würden. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
In einer modernen Welt könnte man auch Father/Mother durch SPouse1/Spouse2 ersetzten oder noch besser die Eltereigenschaft getrennt setzen.
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Liste der Anhänge anzeigen (Anzahl: 1)
So, um eines Vorweg zu nehmen, danke für Eure bisherigen Antworten.
Ich arbeite mich grade erst in die Materie ein. ANFÄNGER!!!! Ich bin wieder in die Delhpipraxis-Community zurückgekehrt, da ich früher hier sehr gute Erfahrungen gemacht habe. Ich entwickle unter OS X mit ![]() Von vorne: Die Beispieldatenbank hat folgende Tabellen:
Was mich also beschäftigt ist zum einen, wie baue ich um meinen Probanden, der stets zentriert im Fokus der grafischen Ausgabe sein soll, alles andere um ihn herum? Deswegen auch die Frage mit den Scrollbars! Vielleicht besteht die Möglichkeit, dass wir hier Step by Step, Tutorialartig so etwas gemeinsam definieren können, das fände ich klasse ;) So würde die grafische Ausgabe zum Beispiel mal ausschauen: |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Was hast du denn bisher?
Zeig mal Quelltext! Der Ansatz von Namenloser mit den Layern ist eigentlich ganz knuffig. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Meine Gedanken (der Einfachheit halber mal eingedeutscht):
Delphi-Quellcode:
Ist das ein prinzipiell falscher Ansatz?
// es gibt mehrere Möglichkeiten der Darstellung, die mir vorschweben
// // 1. der Proband ist eine Partnerschaft // 2. der Proband ist eine einzelne Person // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // zu 1. (Nachteil hier, es werden keine weiteren Partner & Geschwister angezeigt) // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TPerson = class const Width: 200; Height: 75; public Name: String; Vater: TPerson; Mutter: TPerson; procedure Draw(Canvas, TCanvas; x, y: Integer); end; TPartnerschaft = class // im Falle 1. wäre Partnerschaft dann der "Proband" public PartnerA: TPerson; // Mann oder Frau bei gleichgeschlechtlichen Partnerschaften PartnerB: TPerson; // Frau " Kinder: TList<TPerson>; // Reihenfolge der Kinder kann schon bei der SQL-Abfrage erfolgen procedure Draw(Canvas: TCanvas); end; procedure TPerson.Draw(Canvas: TCanvas; x, y: Integer); begin Canvas.DrawRectangle(x, y, Width, Height); // später kann hier noch der Name der Person ausgegeben werden if Vater <> nil and Mother <> nil then begin // Eltern zeichnen dementsprechend versetzt über dem Probanden (x, y müssen angepasst werden) end else if Vater <> nil and Mother = nil then begin // nur Vater zeichnen end else if Vater = nil and Mother <> Nil then begin // nur Mutter zeichnen end; end; procedure TPartnerschaft.Draw(Canvas: TCanvas); begin // hier wird es nun kompliziert, da zunächst alle Kinder gezählt werden müssen, um den Probanden oberhalb zu zentrieren, ABER // sollte die Breite der Elterngenerationen (Eltern, Großeltern) breiter sein, als die der Kinder, so orientiert sich die Ausrichtung an diesen if PartnerA <> nil and PartnerB <> nil then begin // iterativ alle Elterngenerationen zeichnen, aber wie finde ich heraus, wieviele das sind, also durch Iteration? PartnerA.Draw(Canvas, x, y); // ??????? end else if PartnerA <> nil and PartnerB = nil then begin // nur PartnerA zeichnen end else if PartnerA = nil and PartnerB <> Nil then begin // nur PartnerB zeichnen end; for i as Integer = 0 to Kinder.Count do begin // auch hier müssen Koordinaten ausfindig gemacht werden, damit die Kinder unter dem Probanden und jeweils nebeneinander gezeichnet werden können end; end; // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // zu 2. (Partner & Geschwister können angezeigt werden) // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // um diesen soll sich dann alles drehen, also der Stammbaum aufbauen TProband = class(TPerson) public Geschwister: TList<TPerson>; Partner: TList<TPerson>; Kinder: TList<TPerson>; // Draw-Methoden müssten analog zu oben sein end; TStammbaum = class(TCanvas) puclic Proband: TProband; // oder eben TPartnerschaft // Draw-Methoden müssten analog zu oben sein end; |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Wenn ich mir das Bild so anschaue, dann kann man die Daten zu dieser Anzeige hiermit abbilden (die Bilder habe ich jetzt mal weggelassen):
Delphi-Quellcode:
unit Unit2;
interface uses System.Generics.Collections; type TLocationDateTime = record Location: string; Timestamp: TDateTime; end; TPersonViewModel = class private FID: TGUID; FLastName: string; FFirstName: string; FBorn: TLocationDateTime; FDied: TLocationDateTime; public property ID: TGUID read FID write FID; property LastName: string read FLastName; property FirstName: string read FFirstName; property Born: TLocationDateTime read FBorn; property Died: TLocationDateTime read FDied; end; TParentViewModel = class( TPersonViewModel ) private FFather: TPersonViewModel; FMother: TPersonViewModel; public property Father: TPersonViewModel read FFather; property Mother: TPersonViewModel read FMother; end; TChildViewModel = class( TPersonViewModel ) private FEngagedTo: TList<TPersonViewModel>; public property EngagedTo: TList<TPersonViewModel> read FEngagedTo; end; TRelationViewModel = class( TPersonViewModel ) private FChildren: TList<TChildViewModel>; public property Children: TList<TChildViewModel> read FChildren; end; TSelectedPersonViewModel = class( TPersonViewModel ) private FFather: TParentViewModel; FMother: TParentViewModel; FRelations: TList<TRelationViewModel>; FSiblings: TList<TChildViewModel>; public property Father: TParentViewModel read FFather; property Mother: TParentViewModel read FMother; property Siblings: TList<TChildViewModel> read FSiblings; property Relations: TList<TRelationViewModel> read FRelations; end; implementation end. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Bleibt nach wie vor zu klären, wie man den Baum "günstig" grafisch in einer Paintbox ausgibt, sodass er sich auch scrollen lässt (Zoom wäre natürlich super). ;) und, wie lässt es sich umsetzen, bei so einem verschachtelten Modell, dass jeder Eintrag sich auf der Zeichenfläche anklicken lässt? P.S. Was macht EngagedTo? |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Das war nur eine begriffliche Entgleisung und sollte MarriedWith, oder welche Art der Beziehung du dort abbilden möchtest.
Siehe Geschwister und Kinder auf dem Bild, da sind noch 0..2 Personen darunter. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zum Zeichnen:
Die Paintbox in eine ScrollBox packen und schon scrollt das, wenn die ScrollBox kleiner als die Paintbox wird. Die Größe der Paintbox lässt sich einfach berechnen, denn jeder Eintrag hat eine feste Größe und somit ist es fast ein simples Multiplizieren und Addieren. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Hallo lieber Community,
ich möchte dieses Thema erneut aufgreifen, da ich keinen Schritt weitergekommen bin. Wie gesagt, ich bin Hobby-Entwickler und kann daher oft nicht jeden Gedanken problemlos nachvollziehen. Ich sage das nur, damit die Antworten gern „normalofreundlich“ ausfallen, ohne Fachbegriffe bzw. diese erklärt. Zitat:
Zitat:
Was ist in dem Fall, dass eine Ausgangsperson bzw. deren Geschwister mehrere Partner hatte? Die müssen ja dann auch in dem Layer dargestellt werden und die Kinder der einzelnen Beziehungen darunter. Zitat:
Zitat:
Ich würde mich sehr freuen, wenn wir das Step-by-Step behandeln könnten, damit ich es verstehe und dann auch zu dem, juhu, wie ich Mathematik geliebt habe :( , Berechnungen der Koordinaten kommen. Ich danke sehr im Voraus. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Also erst einmal hierzu:
Zitat:
Willst Du hingegen die Möglichkeiten die sich juristisch ergeben können, wirst Du nicht umhin kommen deine Daten in einer solchen (oder ähnlichen) Struktur abzulegen:
Delphi-Quellcode:
Gruß
TRelPerson =class
relatedPersID : tID; reltype : Treltype; end; TPerson = class relpersonslist: tlistofRelPerson; Name: String; end; K-H |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Wie gesagt, mir bereitet eher die Datenstruktur und deren grafische Ausgabe Kopfzerbrechen, ein normaler Baum kommt hier nicht in Frage und ich denke, Namenloser hat mit seiner Graphen-Argumentation recht. Nur ist diese Materie mir völlig fremd. Ich weiß nur, dass man da nicht unbedingt auf Rekursion zurückgreifen muss. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Ich betreibe Ahnenforschung seit 2013 und verstehe also das, was du da machen willst.
Mal abgesehen von der Datenstruktur, die mit dem beispielhaften TFamilyTree und TPerson erstmal völlig ausreichend ist (Adoptivkinder und andere Spezialfälle würde ich mir wirklich für später aufsparen), was genau bereitet dir Kopfzerbrechen? Das reine Zeichnen an und für sich, also Kästchen in der und der Farbe mit Schrift können wir dir anhand von Delphi-Code zeigen, aber wahrscheinlich nicht anhand von Bespielen mit Xojo (wenn du damit noch entwickelst). Wie man etwas anordnen und zeichen kann - also die Frage nach dem Konzept - wäre es gut sich Inspiration bei Ahnenblatt, MyHeritage, Ancestry oder eine der 13264 anderen Stammbaumprogrammen zu holen und das einfach mal nachzuzeichnen. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Diese TFamilyTree Klasse soll vor der grafischen Ausgabe automatisch die benötigte Breite und Höhe des Baums anhand seiner Graphen (Partner, Geschwister, Kinder) und die Anzahl der Generationen in ein Bitmap zeichnen. Anhand der Größe des Bitmap kann man ja dann auch die Größe des Canvas einstellen. Was mich interessiert ist also, wie man das ganze ohne viel manuelles Zutun mathematisch arrangiert.
Delphi-Code geht in Ordnung für mich, die Syntax kann ich lesen und verstehen. Und wie ich mir das optisch vorstelle, davon habe ich im ersten Beitrag auf Seite 2 ein Bild gepostet. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Die Struktur von SirRufo scheint sehr gut. Vielleicht kann man anhand derer die dynamische Berechnung der Positionen der einzelnen Elemente und der sich daraus ergebenden Gesamtbreite und -höhe erklären.
Was mir dort aber noch etwas Kopfzerbrechen bereitet, wie kann man dann mit dieser Struktur mittels MouseMove Event die aktuelle Person abfragen? Denn es gibt ja nicht nur reine TPerson, sondern in anderen Klassen enthaltene TPerson. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Ich möchte dir nicht zu Nahe treten, aber vielleicht ist das Vorhaben auch etwas über deinen Möglichkeiten.
Fang mal ganz klein an. Du brauchst eine Datenstruktur, die erstmal Eltern und Kinder aufnehmen kann. Die haben wir gegeben. Diese füllst du erstmal mit Daten und dann machen wir den allereinfachsten Fall: Wir haben zwei Elternteile und meinetwegen vier Kinder. Schreibe auf den Canvas oben zentriert die Namen der Eltern (Erst Vater, dann Mutter) und in der nächsten "Zeile" (ich nenne es mal so) die Namen der Kinder. Reine Textausgabe. Kästchen, Verbindungslinien und Anfassen und Verschieben heben wir uns mal für später auf. Wenn du das hast, melde dich wieder. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Vielleicht noch als Hinweis:
Weder die verknüpfende Baumstruktur, noch die einzelnen Personen-Klassen sollten irgendwelche Methoden haben die was grafisches machen, wie Draw zum Beispiel. Stattdessen baue dir eine weitere Klasse, z.B. namens TGraphBuilder, der du einen Canvas/Bitmap gibst und dazu die Personenliste, die du darstellen möchtest. Schon wird vieles klarer und einfacher. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Delphi-Quellcode:
Und die Test-Daten:
unit Test;
interface uses System.Generics.Collections; type TPerson = class public Name: String; X, Y: Integer; constructor Create(name: String); procedure SetKoordinaten(x, y: Integer); end; TFamilie = class public Vater: TPerson; Mutter: TPerson; Kinder: TList<TPerson>; constructor Create(vater: TPerson, mutter: TPerson); end; TGraphBuilder = class public Familie: TFamilie; Picture: TCanvas; constructor Create(familie: TFamilie) procedure Draw; end; Const Abstand: Integer = 50; Breite: Integer = 100; Hoehe: Integer = 40; implementation // TPerson constructor TPerson.Create(name: String); begin inherited; Name = name; end; procedure SetKoordinaten(x, y: Integer); begin X = x; Y = y; end; // TFamilie constructor TFamilie.Create(vater: TPerson, mutter: TPerson); begin inherited; Vater = vater; Mutter = mutter; Kinder = TList.Create; end; // TGraphBuilder constructor TGraphBuilder.Create(familie: TFamilie); begin inherited; Familie = familie; end; procedure TGraphBuilder.Draw; var i: Integer; begin Familie.Vater.SetKoordinaten(0, 0); Familie.Mutter.SetKoordinaten(Vater.X + Breite + Abstand, 0); // Vater/Mutter zeichnen Picture.Rectangle(Vater.X, Vater.Y, Vater.X + Breite, Vater.Y + Hoehe); Picture.Rectangle(Mutter.X, Vater.Y, Mutter.X + Breite, Mutter.Y + Hoehe); // Kinder for i:= Low(Familie.Kinder) To High(Familie.Kinder) do begin if i = 0 then Kinder(i).SetKoordinaten(i * Breite, Vater.Y + Hoehe + Abstand); else Kinder(i).SetKoordinaten(i * Breite + Abstand, Vater.Y + Hoehe + Abstand); end; Picture.Rectangle(Kinder(i).X, Kinder(i).Y, Kinder(i).X + Breite, Kinder(i).Y + Hoehe); Picture.Textout(Kinder(i).X, Kinder(i).Y, Kinder(i).Name); end; end; end.
Delphi-Quellcode:
procedure GeneriereTestDaten;
var familie: TFamilie; vater, mutter, proband, kind2, kind3, kind4: TPerson graph: TGraphBuilder; begin // Eltern vater = TPerson.Create('Vater Mustermann'); mutter = TPerson.Create('Mutter Musterfrau'); // Proband proband = TPerson.Create('Max Mustermann'); // Geschwister kind2 = TPerson.Create('Heidi Mustermann') kind3 = TPerson.Create('Friedrich Mustermann') kind4 = TPerson.Create('Jonas Mustermann') // Familie familie = TFamilie.Create(vater, mutter) familie.Kinder.Add(proband) familie.Kinder.Add(kind2) familie.Kinder.Add(kind3) familie.Kinder.Add(kind4) // Familie zeichnen graph = TGraphBuilder.Create(familie); graph.Picture = Canvas1; graph.Draw; end; |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Der Code compiliert sicher nicht.
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Hm, ich sehe jetzt nicht warum mein Beitrag wertvoller werden würde wenn ich angebe warum er nicht compiliert. Der Poster kann es ja selber versuchen und stellt es so schneller fest.
Wie dem auch sei - ich sah nur das "TCanvas", das nirgendwo definiert wurde. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Zitat:
Canvas1 ist ein Canvas auf einer TForm. Vielen Dank. |
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Ach so, tut mir leid. Dass Sie noch nicht mit Delphi entwickelt haben habe ich zwar gelesen, bin dabei aber ausgegangen dass Sie es dann aaber jetzt tun werden. Bei den Inhalten mit "unit", "interface", "uses" und "end." bin ich davon ausgegangen, dass es sich um eine fertige Datei handeln soll.
|
AW: konzeptionelle Gedanken optische Darstellung Stammbaum
Um mal wieder produktiv zu werden, folgender Code für ein leeres Formular als Ausgangsbasis. Damit können wir grundlegende Sachen klären und uns dann später darum kümmern, wie man die gezeichneten Objekte anfassen und verschieben kann. Dazu noch die obligatorischen Verbindungslinien. Vielleicht das als erstes. Wer will, wer hat Ideen?
Delphi-Quellcode:
unit StammbaumFrm;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections; type TPerson = class public Name: string; X, Y: Integer; constructor Create(const AName: string); procedure SetKoordinaten(AX, AY: Integer); end; TFamilie = class public Vater: TPerson; Mutter: TPerson; Kinder: TList<TPerson>; constructor Create(AVater: TPerson; AMutter: TPerson); end; TGraphBuilder = class public Familie: TFamilie; Picture: TCanvas; constructor Create(AFamilie: TFamilie); procedure Draw; end; const Abstand: Integer = 50; Breite: Integer = 100; Hoehe: Integer = 40; type TForm2 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormPaint(Sender: TObject); procedure FormResize(Sender: TObject); private Familie: TFamilie; graph: TGraphBuilder; FBitmap: TBitmap; public end; var Form2: TForm2; implementation {$R *.dfm} // TPerson constructor TPerson.Create(const AName: string); begin inherited Create; name := AName; end; procedure TPerson.SetKoordinaten(AX, AY: Integer); begin X := AX; Y := AY; end; // TFamilie constructor TFamilie.Create(AVater: TPerson; AMutter: TPerson); begin inherited Create; Vater := AVater; Mutter := AMutter; Kinder := TList<TPerson>.Create; end; // TGraphBuilder constructor TGraphBuilder.Create(AFamilie: TFamilie); begin inherited Create; Familie := AFamilie; end; procedure TGraphBuilder.Draw; var i: Integer; TextSize: TSize; Vater, Mutter, Kind: TPerson; X, Y, W, H: Integer; PersonName: string; begin Vater := Familie.Vater; Mutter := Familie.Mutter; Familie.Vater.SetKoordinaten(0, 0); X := Vater.X + Breite + Abstand; Familie.Mutter.SetKoordinaten(X, 0); // Picture.Font.Name := 'Tahoma'; // Picture.Font.Size := 12; // Picture.Font.Color := clBlack; // Vater/Mutter zeichnen PersonName := Vater.Name; TextSize := Picture.TextExtent(PersonName); X := Vater.X; Y := Vater.Y; W := X + Breite; H := Y + Hoehe; Picture.Rectangle(X, Y, W + TextSize.cx div 2, H); X := X + 5; Y := Y + TextSize.cy div 2; Picture.Textout(X, Y, PersonName); PersonName := Mutter.Name; TextSize := Picture.TextExtent(PersonName); X := Mutter.X + TextSize.cx div 2; Y := Mutter.Y; W := X + Breite; H := Y + Hoehe; Picture.Rectangle(X, Y, W + TextSize.cx div 2, H); X := X + 5; Y := Y + TextSize.cy div 2; Picture.Textout(X, Y, PersonName); // Kinder for i := 0 to Familie.Kinder.Count - 1 do begin Kind := Familie.Kinder[i]; PersonName := Kind.Name; TextSize := Picture.TextExtent(PersonName); if i = 0 then begin X := i * Breite; Y := Vater.Y + Hoehe + Abstand; Kind.SetKoordinaten(X, Y); end else begin X := i * (Breite + (2 * Abstand)); Y := Vater.Y + Hoehe + Abstand; Kind.SetKoordinaten(X, Y); end; Y := Kind.Y; W := X + TextSize.cx + 10; H := Y + Hoehe; Picture.Rectangle(X, Y, W, H); X := X + 5; Y := Y + TextSize.cy div 2; Picture.Textout(X, Y, PersonName); end; end; procedure TForm2.FormCreate(Sender: TObject); var Vater, Mutter, proband, kind2, kind3, kind4: TPerson; begin // Eltern Vater := TPerson.Create(' Vater Mustermann '); Mutter := TPerson.Create(' Mutter Musterfrau '); // Proband proband := TPerson.Create(' Max Mustermann '); // Geschwister kind2 := TPerson.Create(' Heidi Mustermann '); kind3 := TPerson.Create(' Friedrich Mustermann '); kind4 := TPerson.Create(' Jonas Mustermann '); // Familie Familie := TFamilie.Create(Vater, Mutter); Familie.Kinder.Add(proband); Familie.Kinder.Add(kind2); Familie.Kinder.Add(kind3); Familie.Kinder.Add(kind4); FBitmap := TBitmap.Create; // Familie zeichnen graph := TGraphBuilder.Create(Familie); end; procedure TForm2.FormPaint(Sender: TObject); begin graph.Picture := FBitmap.Canvas; // Canvas1; graph.Draw; Self.Canvas.Draw(0, 0, FBitmap); end; procedure TForm2.FormResize(Sender: TObject); begin FBitmap.SetSize(Self.ClientWidth, Self.ClientHeight); end; end. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:47 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz