Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Baumstruktur selber zeichnen (https://www.delphipraxis.net/189640-baumstruktur-selber-zeichnen.html)

erich.wanker 4. Jul 2016 14:01

Baumstruktur selber zeichnen
 
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... :pale:
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 :stupid:
Erich

Sir Rufo 4. Jul 2016 14:12

AW: Baumstruktur selber zeichnen
 
Schau doch mal bei der Rachel vorbei. Ist zwar nicht Delphi aber recht gut beschrieben, was sie da macht.

https://rachel53461.wordpress.com/

ibp 4. Jul 2016 14:26

AW: Baumstruktur selber zeichnen
 
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;

himitsu 4. Jul 2016 14:33

AW: Baumstruktur selber zeichnen
 
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.

Sir Rufo 4. Jul 2016 14:43

AW: Baumstruktur selber zeichnen
 
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)

ibp 4. Jul 2016 14:54

AW: Baumstruktur selber zeichnen
 
Zitat:

Zitat von Sir Rufo (Beitrag 1341913)
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;

Lemmy 4. Jul 2016 16:06

AW: Baumstruktur selber zeichnen
 
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

Sir Rufo 4. Jul 2016 17:57

AW: Baumstruktur selber zeichnen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hattest du an so etwas gedacht
Anhang 45558

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.
  • Delphi-Quellcode:
    TFooData
    hat die Daten
  • Delphi-Quellcode:
    TFooModel
    stellt die Beziehungen zwischen den Daten her
  • Delphi-Quellcode:
    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


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:12 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