AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Baumstruktur selber zeichnen

Ein Thema von erich.wanker · begonnen am 4. Jul 2016 · letzter Beitrag vom 4. Jul 2016
Antwort Antwort
Benutzerbild von erich.wanker
erich.wanker

Registriert seit: 31. Jan 2008
Ort: im schönen Salzburger Land
453 Beiträge
 
Delphi XE4 Professional
 
#1

Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 15:01
Hallo,

Ich hab in einer Datenbank Datensätze, die untereinander wie in einer Baumstruktur verknüpft sind

Jeder Datensatz hat eine Eindeutige Nummer --> Feld (INR)
und eine Verknüpfung zur INR eines anderen (vorgelagerten) Datensatzes --> Feld (PR)
Root-Datensätze haben als PR = 1


Es gibt maximal 10 Ebenen:

ich könnte die ganze Datenbank durchgehen mit:
Query_1 select * from DB where PR = 1 (Root)
Query_1 onDataChange = while not query_1.EOF -> Query_2 select * from DB where PR = INR von Query_1
Query_2 onDataChange = while not query_2.EOF -> Query_3 select * from DB where PR = INR von Query_2
Query_3 onDataChange = while not query_3.EOF -> Query_4 select * from DB where PR = INR von Query_3
Query_4 onDataChange = while not query_4.EOF -> Query_5 select * from DB where PR = INR von Query_4
Query_5 onDataChange = while not query_5.EOF -> Query_6 select * from DB where PR = INR von Query_5
...


Ich weiß, wie viele UnterObjekte ein Objekt besitzt (recordcount..)

Ich weiß, wie groß ein Datensatz dargestellt werden soll (panel mit 130px mal 25px inkl. Beschriftung) mit OnClick -> eindeutige INR des Datensatzes wird an procedure gesendet

Ich weiß aber NICHT, in welcher Ebene der Datensatz ist - ich hab kein Datenbankfeld, wo das ersichtlich wäre
(hat sich bis dato immer als Knackpunkt erwiesen)



Jetzt will ich die Datensätz (als Panel) wie in einer Baumstruktur zeichnen...
Im Vorfeld dachte ich mir, das wird ganz einfach zu lösen sein .. (naiv)
Die Probleme haben aber überhand genommen

Gibt es dafür einen fertigen Algorithmus oder was brauchbares, der mir die Position zum Zeichnen berechnet?


Vielen Dank für Tipps und Hinweise - wie man sowas richtig macht
Erich
Erich Wanker - for life:=1971 to lebensende do begin ..
O
/H\
/ \
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 15:12
Schau doch mal bei der Rachel vorbei. Ist zwar nicht Delphi aber recht gut beschrieben, was sie da macht.

https://rachel53461.wordpress.com/
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von ibp
ibp

Registriert seit: 31. Mär 2004
Ort: Frankfurt am Main
1.511 Beiträge
 
Delphi 7 Architect
 
#3

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 15:26
Hallo,
ist zwar nicht optimal aber als Grundstruktur sollte es so gehen...
Delphi-Quellcode:
procedure printTree(const aPR, alevel:integer);
var
 aQuery:TQuery
begin
  aQuery:=TQuery.Create(nil);
  try
    aQuery.Database := 'myTrallalal'
    aQuery.SQL.Text := 'select INR,aa,bb,cc from myTable where PR=:pPR';
    aQuery.params[0].asinteger = aPR;
    aQuery.Open;
    while not aQuery.eof do
    begin
      // hier jetzt print Daten, den aLevel kann man
      // als Multiplikator für den Einzug benutzen
      ....
      printTree(aQuery.fieldbyname('INR',aLevel+1); // rekursiver Aufruf der Childs
      aQuery.next;
    end;
  finally
    aQuery.Free;
  end;
end


begin
  ..
  printTree(1,0);
  ..
end;
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.017 Beiträge
 
Delphi 12 Athens
 
#4

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 15:33
Wenn dein DBMS rekursive SQL-Befehle kennt, dann kannst du für jeden Knoten zählen wie viele Eltern der hat und schon weißt du die Tiefe.

Außerdem fragst du das selber schon rekursiv ab, also mußt du dir nur merken in welcher Ebene du gerade bist und der aktuelle Knoten ist dann Eines tiefer.



Im Grunde kann man auch gleich DB-seitig die Ebene ermitteln und zusammen mit den Eltern alle Datensätze fertig sortiert ausgeben und muß dann nur noch in Ruhe zeichnen, ohne das über mehrere SELECTS abzufragen.




Ich hatte soeinen Code auch schonmal gesehn, wo man das Query auf CachedUpdate aktiviert hatte.
Dann nahm man sich einen Datensatz, ging per Locate so lange hoch, bis man keinen Elternknoten fand.
Danach dann rekursiv im selben Query per Locate die Kinder suchen und sie löschte, wenn eingefügt. (man könnte sich auch in einer Liste speichern, was schon verarbeitet wurde)
Und danach das wiederholen, bis es keine Datensätze mehr gibt. (das Letzte war, falls es Probleme in der Stuktur/Daten gibt, damit dann dennoch alles ausgegeben wird)





Ich hatte mir da auch or Kuzrem ein böses SELECT in Postgres geschrieben, um in einem DBGrid die selbe Reihenfolge zu haben, wie im TreeView.

SQL-Code:
SELECT
  sd_id, sd_name,
  sd_parentid,
  ...

  (SELECT array_to_string(array_agg(to_char(x_id, 'FM00009')), '-')::VARCHAR(250) FROM (
    WITH RECURSIVE temp (x_depth, x_id, x_parentid) AS (
      (SELECT 0, x.sd_id, x.sd_parentid
        FROM SettingsDyn AS x
        WHERE x.sd_id = SettingsDyn.sd_id
        UNION
        SELECT 666, 0, NULL) -- virtueller Wurzel-Knoten
      UNION ALL
      (SELECT x_depth+1, x.sd_id, x.sd_parentid
        FROM SettingsDyn AS x
        JOIN temp ON x_parentid = x.sd_id
        WHERE x_depth < 32)
    ) SELECT * FROM temp ORDER BY x_depth DESC
  ) AS temp) AS sd_order

FROM SettingsDyn
ORDER BY sd_order
[*] max(x_depth) ergäbe die Tiefe

SQL-Code:
(WITH RECURSIVE temp (x_depth, x_id, x_parentid) AS (
  (SELECT 0, x.sd_id, x.sd_parentid
    FROM SettingsDyn AS x
    WHERE x.sd_id = SettingsDyn.sd_id)
  UNION ALL
  (SELECT x_depth+1, x.sd_id, x.sd_parentid
    FROM SettingsDyn AS x
    JOIN temp ON x_parentid = x.sd_id
    WHERE x_depth < 32)
) SELECT max(x_depth) FROM temp) AS DieTiefe
[*] oder x_id gibt einfach ALLE Eltern zurück (hier inkl. sich selber), was man dann sonstwie kombinieren kann,
hier z.B. als ein String, der für die Sortierung nutzbar ist. (der Tree ist über die IDs sortiert, also stehen hier die IDs der Prarents in der richtigen Reihenfolge im String)

Code:
00000
00001
00001_00002
00001_00002_00003
00001_00004
00002
....
Hier könnte man auch auf x_depth verzichtgen und über Length(sd_order)/6 die Tiefe holen, falls/da man nur eine Spalte zurückgeben kann.
Sonst müsste man das besser in eine Funktion auslagern oder es als LATERAL JOIN anhängen.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu ( 4. Jul 2016 um 15:38 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#5

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 15:43
Der Level (also die Y Position) ist nicht so das Problem. Die X Position ist da etwas komplexer.

Und die Struktur kann er schon lesen. Es geht um die Ermittlung der X Position der jeweiligen Elemente. (oh, nee, das Lesen der Struktur ist auch noch ein Knackpunkt)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von ibp
ibp

Registriert seit: 31. Mär 2004
Ort: Frankfurt am Main
1.511 Beiträge
 
Delphi 7 Architect
 
#6

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 15:54
Der Level (also die Y Position) ist nicht so das Problem. Die X Position ist da etwas komplexer.

Und die Struktur kann er schon lesen. Es geht um die Ermittlung der X Position der jeweiligen Elemente. (oh, nee, das Lesen der Struktur ist auch noch ein Knackpunkt)
die braucht er doch auch nur mitzuführen..oder macht aus der Prozedur eine Funktion und gibt diesen dann zurück


Delphi-Quellcode:
function printTree(const aPR, alevelY:integer; alevelX:integer):integer;
var
 aQuery:TQuery
begin
  aQuery:=TQuery.Create(nil);
  try
    aQuery.Database := 'myTrallalal'
    aQuery.SQL.Text := 'select INR,aa,bb,cc from myTable where PR=:pPR';
    aQuery.params[0].asinteger = aPR;
    aQuery.Open;
    while not aQuery.eof do
    begin
      inc(alevelX);

      // der Datensatz hat jetzt die Position (alevelx, alevely)

      // hier jetzt print Daten, den aLevel kann man
      // als Multiplikator für den Einzug benutzen
      ....
      alevelx:= printTree(aQuery.fieldbyname('INR',alevelY+1, alevelX); // rekursiver Aufruf der Childs
      aQuery.next;
    end;
  finally
    aQuery.Free;
  end;

  result := alevelx;
end


begin
  ..
  printTree(1,0,0);
  ..
end;

Geändert von ibp ( 4. Jul 2016 um 16:11 Uhr)
  Mit Zitat antworten Zitat
Lemmy

Registriert seit: 8. Jun 2002
Ort: Berglen
2.364 Beiträge
 
Delphi 10.3 Rio
 
#7

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 17:06
Es gab da mal einen 3-teiligen Artikel von Holger Klemt, ich meine im Entwickler, vor so 15 Jahren, in dem er das für Firebird recht einfach in einer StoredProcedure implementiert hat. Diese hat sich selbst rekursiv aufgerufen und den Level damit selbst berechnet. Von daher wäre die Abfrage (wenn bei deiner DB StoredProceures unterstützt werden, recht einfach möglich) - falls du das brauchst kann ich mal nachschauen wo ich das Teil habe.

Und bzgl. der Darstellung: Müssen das unbedingt Panels sein die du irgend wo rein positionierst? Reicht für die Darstellung ggf. der VIrtualStringtree nicht auch aus?

Grüße
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#8

AW: Baumstruktur selber zeichnen

  Alt 4. Jul 2016, 18:57
Hattest du an so etwas gedacht
treestructure.png

Source und EXE im Anhang.

Zur Info:

Da ich nur ein kleiner Geist bin und ich mich immer nur auf eins konzentrieren kann, habe ich hierfür ein paar Klassen angelegt.
  • TFooData hat die Daten
  • TFooModel stellt die Beziehungen zwischen den Daten her
  • TFooViewModel kümmert sich um die Aufbereitung für die Anzeige (Berechnung von Width, Level, Position)
  • Die Form klatscht den ganzen Rotz dann in eine Paintbox abhängig von dem, was das ViewModel sich da zusammengerechnet hat
Angehängte Dateien
Dateityp: zip TreeStructure.zip (885,0 KB, 21x aufgerufen)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:15 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz