Page 1 of 2 1 2 LastLast
Results 1 to 15 of 27

Thread: Virtualtree met Subnodes

Hybrid View

  1. #1

    Virtualtree met Subnodes

    ivroegah, had ik een programma voor een soort News reader, was dus meer dan 10 jaar oud is denk ik.
    En ben de source ervan ook kwijt.

    Ik kan me voor geen meter meer herinneren hoe ik Sub nodes aan een normale node kan doen.

    Ik heb een column die ik wou gebruiken, dat bepaalt alles voor de main node en dan de andere er aanvast the zetten, maar kom er niet uit.
    iemand die me kan helpen?

    Heb bv.

    Nam, mynindex
    Piet 1
    Klaas 2
    Pietje 1
    Peter 1

    Dus dan wordt het

    + Piet
    - Pietje
    - Peter
    + Klaas

    Dank u

  2. #2
    Wat heb je en wat gaat er mis?
    1+1=b

  3. #3
    Dit is wat ik momenteel heb.


    Code:
    // butten om de VirtualTree te laden
    procedure Tfrmmain.dxBarButton3Click(Sender: TObject);
    var
    i : integer;
    Column: TSWITCHViewerColumns;
    begin
    
     FCommandText := 'SELECT * FROM SWITCHDATA';
     
       try
        SwitchTree.BeginUpdate();
        try
          switchquery1.Close();
          switchquery1.SQL.Text := FCommandText ;
          for I := 0 to FCommandParams.Count - 1 do
          switchquery1.Params[i].AsString := FCommandParams[I];
          switchquery1.Open();
          InitSwitchFields();
          SwitchTree.RootNodeCount := switchquery1.RecordCount;
          finally
        SwitchTree.EndUpdate();
        end;
      finally
      end;
    end;
    
    
    
    
    procedure Tfrmmain.switchreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    var
    fsize11: string;
    fsize2 : int64;
    fsize22: string;
    scountry : string;
    datechange : string;
    categories : string;
    begin
    fsize22:='';
    fsize11 := FormatSize(switchquery1.Fields[FLD_SIZE].AsLargeInt, rsMB);
    datechange:=FswitchFields[SWITCH_RELEASEDATE].AsString;
     if length(datechange)=8 then
        begin
        datechange:=copy(datechange,5,2)+'/'+copy(datechange,7,2)+'/'+copy(datechange,1,4);
        end;
    
    categories:=FswitchFields[SWITCH_CATEGORY].AsString;
    
    case TswitcRomViewerColumns(Column) of
     SWITCH_NAME           : CellText := FswitchFields[SWITCH_NAME].AsString;
     SWITCH_ID             : CellText := FswitchFields[SWITCH_ID].AsString;
     SWITCH_REGION         : CellText := FswitchFields[SWITCH_REGION].AsString;
     SWITCH_RELEASEDATE    : CellText := datechange;
     SWITCH_CATEGORY       : CellText := categories;
     SWITCH_SIZE              : CellText := fsize11;
     SWITCH_FILENAME      : CellText := FswitchFields[SWITCH_FILENAME].AsString;
     SWITCH_STATUS         : CellText := appconsts.CStatusTab[FswitchFields[SWITCH_STATUS].AsInteger];
     SWITCH_COMBINEID   : CellText := FswitchFields[SWITCH_COMBINEID].AsString;    // als dit veld hetzelfde is als the SWITCH_ID dan moet het een child node worden.
     else
      CellText := Format('not implemented (column: %d)', [Column]);
     end;
    
    end;

  4. #4
    ok, ben opnieuw opgestart en hebt dit nu


    Code:
    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option,
      FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
      FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, FireDAC.UI.Intf,
      FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Phys, FireDAC.VCLUI.Wait,
      FireDAC.Comp.Client, Vcl.StdCtrls, VirtualTrees, Data.DB, FireDAC.Comp.DataSet,
      FireDAC.Phys.SQLite, FireDAC.Phys.SQLiteDef, FireDAC.Stan.ExprFuncs,
      Generics.Collections,system.ioutils;
    
    
    type
      TForm1 = class(TForm)
        SwitchQuery: TFDQuery;
        dbconnection: TFDConnection;
        SwitchTree: TVirtualStringTree;
        procedure FormCreate(Sender: TObject);
        procedure SwitchTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
          Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
        procedure FormDestroy(Sender: TObject);
      private
        procedure BuildTree;
        function NodeWithBaseName (const BaseName : string) : PVirtualNode;
      public
    
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    //---------------------------------
    
    function TForm1.NodeWithBaseName (const BaseName: string): PVirtualNode;
    var
      SavedRecNo : Integer;
      Node : PVirtualNode;
    begin
      Result := nil;
      SavedRecNo := SwitchQuery.RecNo;
      try
        Node := SwitchTree.GetFirst;
        while Node.IsAssigned do
          begin
            SwitchQuery.RecNo := Node.GetData<Integer>;
            if SwitchQuery.Fields.FieldByName ('Basename').AsString = BaseName then
              Exit (Node);
            Node := SwitchTree.GetNext (Node);
          end;
      finally
        SwitchQuery.RecNo := SavedRecNo;
      end;
    end;
    
    //---------------------------------
    
    procedure TForm1.BuildTree;
    begin
      SwitchQuery.First;
      while (not SwitchQuery.Eof) do
      begin
        if SwitchQuery.Fields.FieldByName ('ID').AsString.EndsWith ('000') then
          SwitchTree.AddChild (nil, Pointer (SwitchQuery.RecNo));
        SwitchQuery.Next;
      end;
    
      SwitchQuery.First;
      while (not SwitchQuery.Eof) do
      begin
        if not SwitchQuery.Fields.FieldByName ('ID').AsString.EndsWith ('000') then
          SwitchTree.AddChild (NodeWithBaseName (SwitchQuery.Fields.FieldByName ('Basename').AsString), Pointer (SwitchQuery.RecNo));
        SwitchQuery.Next;
      end;
    end;
    
    //---------------------------------
    
    procedure TForm1.SwitchTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    var
      SavedRecNo : Integer;
    begin
      if not Column in [0..3] then Exit;
      SavedRecNo := SwitchQuery.RecNo;
      try
        SwitchQuery.RecNo := Node.GetData<Integer>;
        CellText := SwitchQuery.Fields[Column].AsString;
      finally
        SwitchQuery.RecNo := SavedRecNo;
      end;
    end;
    
    //---------------------------------
    
    procedure TForm1.FormCreate (Sender: TObject);
    begin
      SwitchTree.NodeDataSize := SizeOf (Integer);
      dbconnection.Params.Database := '..\..\database.db';
      dbconnection.Params.Values['FailIfMissing'] := 'False';
      dbconnection.Params.Values['SharedCache'] := 'True';
      dbconnection.Open;
    
      SwitchQuery.SQL.Text := 'SELECT * FROM SWITCHDATA';
      SwitchQuery.Open;
      SwitchQuery.Last;
    
      BuildTree;
    end;
    
    //---------------------------------
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      dbconnection.Close;
    end;
    
    //---------------------------------
    
    end.
    het werkt maar de builtTree procedure is heel erg traag.
    Is er een manier om dat sneller te maken?
    Had a beginupdat gebruikt, maar dat maakt niks uit

    dank u.

  5. #5
    Tja, je loopt 2 keer door je dataset heen. De tweede keer doe je voor elke rij nog een keer een loop door je nodes om de node met een basename te zoeken. Daarin zitten ook weer dure stringoperaties én verspringingen in je dataset. Ik weet het niet precies, maar het zou me niets verbazen als je voor een paarhonderd rijen uiteindelijk een miljoen keer je querycomponent aanspreekt.

    Mogelijke manieren om dat sneller te maken zijn:

    - Je zou al snelheidswinst halen als je in plaats van het recno een objectje zou maken dat je koppelt aan de node. Daarin kan je het recno opslaan, maar ook de basename en andere info. Dan hoeft NodeWithBaseName alleen maar door de nodes te zoeken zonder ook in de dataset heen en weer te springen.

    - Het query-resultaat sorteren, zodat de parent en z'n children achter elkaar staan. Dan hoef je niet steeds terug te zoeken naar de juiste parent. De laatst toegevoegde parent is dan de parent van je nieuwe node.
    Sorteren zelf kan ook duur en traag zijn, dus die is niet per se de heilige graal. Als je dat kan doen zonder te moeten knippen in ID's is het alleen maar beter. Sorteren kan mogelijk in de dataset zelf (TClientDataSet heeft 'indexes' die daarbij kunnen helpen), of in de achterliggende query (order by clause, goede indexen kunnen helpen bij het sorteren, afhankelijk van de database en de structuur van de query), of door de data in records of objecten in te lezen en deze zelf te sorteren.

    - Eigen dictionaries gebruiken om te helpen met het opzoeken. Een dictionary van basenames met hun node, zorgt ervoor dat je niet steeds terug hoeft te kijken naar je dataset en daar een platte loop op moet doen.
    1+1=b

  6. #6
    Allereerst, start je BuildTree met SwitchTree.BeginUpdate en eindig met SwitchTree.EndUpdate.

    Verder: maak een hash stringlist (kan ook een sorted TStringList zijn; ook al gebruikt die quick sort wat trager is dan een hash index). Laten we aannemen dat je die FIndex noemt.
    Elke node die je aan de tree toevoegt voeg je toe aan je index (bijv FIndex.AddObject(SwitchQuery.Fields.FieldByName ('Basename').AsString, Node)

    In NodeWithBaseName gebruik je dan:
    Code:
    idx := FIndex.IndexOf(BaseName);
    if idx <> -1 then
      result := PVirtualNode(FIndex.Objects[idx])
    else
      result := nil;
    Dat alleen zal je code al drastisch versnellen

  7. #7
    Hallo,

    I heb me code aangepast, en het is nu wat sneller,
    maar nu worden er ge
    en subnodes meer aangemaakt, en in het resultaat zijn de waardes for the columns opgeschoven.
    Waar ik een naam had, heb ik nu een basename met wat onbekende data en waar een basename moet zijn, is het leeg.
    En zo zijn er nog wel een paar.


    Code:
    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option,
      FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
      FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, FireDAC.UI.Intf,
      FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Phys, FireDAC.VCLUI.Wait,
      FireDAC.Comp.Client, Vcl.StdCtrls, VirtualTrees, Data.DB, FireDAC.Comp.DataSet,
      FireDAC.Phys.SQLite, FireDAC.Phys.SQLiteDef, FireDAC.Stan.ExprFuncs,
      Generics.Collections,system.ioutils;
    
    
    type
      TForm1 = class(TForm)
        SwitchQuery: TFDQuery;
        dbconnection: TFDConnection;
        SwitchTree: TVirtualStringTree;
        procedure FormCreate(Sender: TObject);
        procedure SwitchTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
          Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
        procedure FormDestroy(Sender: TObject);
      private
    
        FIndex : Tstringlist;
    
        procedure BuildTree;
        function NodeWithBaseName (const BaseName : string) : PVirtualNode;
      public
    
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    //---------------------------------
    
    function TForm1.NodeWithBaseName (const BaseName: string): PVirtualNode;
    var
      SavedRecNo : Integer;
      Node : PVirtualNode;
      idx  : Integer;
    begin
    
     idx := FIndex.IndexOf(BaseName);
     if idx <> -1 then
        result := PVirtualNode(FIndex.Objects[idx])
        else
        result := nil;
    
    //  Result := nil;
    //  SavedRecNo := SwitchQuery.RecNo;
    //  try
    //    Node := SwitchTree.GetFirst;
    //    while Node.IsAssigned do
    //      begin
    //        SwitchQuery.RecNo := Node.GetData<Integer>;
    //        if SwitchQuery.Fields.FieldByName ('Basename').AsString = BaseName then
    //          Exit (Node);
    //        Node := SwitchTree.GetNext (Node);
    //      end;
    //  finally
    //    SwitchQuery.RecNo := SavedRecNo;
    //  end;
    end;
    
    //---------------------------------
    
    procedure TForm1.BuildTree;
    var
    Node : Tobject;
    begin
      SwitchTree.BeginUpdate;
      SwitchQuery.First;
      while (not SwitchQuery.Eof) do
      begin
    
      FIndex.AddObject(SwitchQuery.Fields.FieldByName ('Basename').AsString, Node);
    
        if SwitchQuery.Fields.FieldByName ('ID').AsString.EndsWith ('000') then
          SwitchTree.AddChild (nil, Pointer (SwitchQuery.RecNo));
        SwitchQuery.Next;
      end;
    
      SwitchQuery.First;
      while (not SwitchQuery.Eof) do
      begin
        if not SwitchQuery.Fields.FieldByName ('ID').AsString.EndsWith ('000') then
          SwitchTree.AddChild (NodeWithBaseName (SwitchQuery.Fields.FieldByName ('Basename').AsString), Pointer (SwitchQuery.RecNo));
        SwitchQuery.Next;
      end;
    
    SwitchTree.EndUpdate;
    end;
    
    //---------------------------------
    
    procedure TForm1.SwitchTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    var
      SavedRecNo : Integer;
    begin
      if not Column in [0..3] then Exit;
      SavedRecNo := SwitchQuery.RecNo;
      try
        SwitchQuery.RecNo := Node.GetData<Integer>;
        CellText := SwitchQuery.Fields[Column].AsString;
      finally
        SwitchQuery.RecNo := SavedRecNo;
      end;
    end;
    
    //---------------------------------
    
    procedure TForm1.FormCreate (Sender: TObject);
    begin
      SwitchTree.NodeDataSize := SizeOf (Integer);
      dbconnection.Params.Database := '..\..\database.db';
      dbconnection.Params.Values['FailIfMissing'] := 'False';
      dbconnection.Params.Values['SharedCache'] := 'True';
      dbconnection.Open;
    
      SwitchQuery.SQL.Text := 'SELECT * FROM SWITCHDATA';
      SwitchQuery.Open;
      SwitchQuery.Last;
      FIndex:=tstringlist.Create;
      BuildTree;
    end;
    
    //---------------------------------
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      dbconnection.Close;
    end;
    
    //---------------------------------
    
    end.

  8. #8
    Kan je een sampletje van die SwitchData tabel geven, zodat we de structuur zien, en aangeven hoeveel rijen je ongeveer in totaal hebt?

    En misschien een screenshot van de werkende, maar te trage versie, zodat we kunnen zien wat je wil bereiken?
    1+1=b

  9. #9
    Zoiets?

    Code:
    procedure TForm1.BuildTree;
    var
      Node : PVirtualNode;
    begin
      SwitchTree.BeginUpdate;
      try
        SwitchQuery.First;
        while (not SwitchQuery.Eof) do
        begin
          if SwitchQuery.Fields.FieldByName ('ID').AsString.EndsWith ('000') then
          begin
            Node := SwitchTree.AddChild(nil, Pointer (SwitchQuery.RecNo));
            FIndex.AddObject(SwitchQuery.Fields.FieldByName ('Basename').AsString, Node);
          end;
          SwitchQuery.Next;
        end;
    
        SwitchQuery.First;
        while (not SwitchQuery.Eof) do
        begin
          if not SwitchQuery.Fields.FieldByName ('ID').AsString.EndsWith ('000') then
          begin
            Node := SwitchTree.AddChild (NodeWithBaseName (SwitchQuery.Fields.FieldByName ('Basename').AsString), Pointer (SwitchQuery.RecNo));
            FIndex.AddObject(SwitchQuery.Fields.FieldByName ('Basename').AsString, Node);
          end;
          SwitchQuery.Next;
        end;
      finally
        SwitchTree.EndUpdate;
      end;
    end;
    Dus na het aanmaken van de node, de node aan de FIndex toevoegen. De unit VirtualTrees heb ik niet, maar ik neem aan dat SwitchTree.AddChild een PVirtualNode terug geeft

  10. #10
    In aanvulling op mijn vorige post. Na het aanmaken van de FIndex in FormCreate moet je de de sorted property nog op True zetten.

    Code:
    FIndex:=tstringlist.Create;
    FIndex.Sorted := True;
    En in BuildTree zou ik ook starten met FIndex.Clear; aan te roepen, maar als je BuildTree toch maar 1 keer aanroept dan is dat niet zo "heel" belangrijk.

  11. #11
    Hier is een kopie van de database. het zijn 35 rijen en ongeveer 14000 records


    Alle ID's that eindig met 000 is the hoofd regel, alles dat niet met 000 eindigd is the Chilld. en dan word are naar de basename gekeken of die hetzelfde is.


    Hier is het hele project + database https://www77.zippyshare.com/v/GSUCcrpp/file.html

    Ik kan hem er niet aanhangen.. limiet is 100K.

  12. #12
    Ah, maar dit was wel de foute versie? De namen en regions komen me wat vreemd over. Overigens lijkt de data ook niet volgens verwachting te zijn. Er zijn twee ids die eindigen op 000, die dezelfde basename hebben. Het vinden van "de" node die bij dat id hoort, is dan niet echt mogelijk, want dat kunnen er meerdere zijn.
    Dat valt overigens niet op, omdat de stringlist dubbelen toestaat. Als je FIndex.Duplicated := dupError zet, dan zie je het op een bepaald moment mis gaan.

    De vreemde waarden lijken te komen omdat de dataset veel meer kolommen bevat. Door specifieke kolommen op te halen in de query (overeenkomstig met de kolom-volgorde van de treeview), lijkt het al een stuk beter te zijn:

    SELECT id, name, region, basename FROM SWITCHDATA

    Daarna valt het nog op dat elke eerste childnode een id is dat eindigt op 800 en verder geen titel of region lijkt te hebben. Als er meedere subnodes zijn, dan hebben die wel tekst.
    Last edited by GolezTrol; 23-Jun-20 at 20:37.
    1+1=b

  13. #13
    Even een stopwatch toegevoegd en de laatste aanpassing van havezet meegenomen:

    (System.Diagnostics toevoegen aan de uses).

    Als ik het meet dan duurde het opbouwen van de tree 8 seconden. Na het op sorted zetten van de stringlist, waardoor hij binary search kan doen, is dat 100ms. Dit is exclusief het uitvoeren van de query zelf.
    Code:
    procedure TForm1.FormCreate (Sender: TObject);
    var
      s: TStopWatch;
    begin
      SwitchTree.NodeDataSize := SizeOf (Integer);
      dbconnection.Params.Database := '..\..\database.db';
      dbconnection.Params.Values['FailIfMissing'] := 'False';
      dbconnection.Params.Values['SharedCache'] := 'True';
      dbconnection.Open;
    
      SwitchQuery.SQL.Text := 'SELECT * FROM SWITCHDATA';
      SwitchQuery.Open;
      SwitchQuery.Last;
      s := TStopWatch.StartNew;
      FIndex:=tstringlist.Create;
      FIndex.Sorted := True;
      BuildTree;
      ShowMessage(s.ElapsedMilliseconds.ToString());
    end;
    En de memory leak die je hebt is op te lossen door FIndex.Free in je FormDestroy method toe te voegen.
    Last edited by GolezTrol; 23-Jun-20 at 20:13.
    1+1=b

  14. #14
    Quote Originally Posted by GolezTrol View Post
    Als ik het meet dan duurde het opbouwen van de tree 8 seconden. Na het op sorted zetten van de stringlist, waardoor hij binary search kan doen, is dat 100ms.
    Niet slecht, maak dat moet nog sneller kunnen. Maar ik ben vooral benieuwd hoe lang het duurt als je FIndex als type THashedStringList (uit unit System.IniFiles) declareert. Hoe groter de dataset, hoe meer rendement er is te behalen met de hash tables.

    PS. Ik zou het graag zelf willen proberen, maar die download site wil van alles installeren en ik zit niet op een "speel" omgeving.
    Last edited by havezet; 24-Jun-20 at 08:19.

  15. #15
    Klopt alles met 800 zijn zeg maar updates, en die hebben geen naam. Ik zou bv. de naam van het 000 record kunnen gebruiken
    Alles was niet 000 of 800 is zijn DLC items. for the 000 record.

    Ben net klaar met me nieuwe laptop te installeren, dus kan ik weer met Delphi aan de slag.


    edit,
    Er is en ID dat heeft het volledige veld.
    dan is er een BaseName die maar 8 of 9 karakters zijn. (is het ID - 3 ) aangezien de 1st 8/9 karakters altijd hetzelfde zijn voor de titel (name) kan je makkelijk
    alle ID's pakken die met 000 eindigen en dan alle sub nodes die zijn dan ID <> 000 en BaseName = base ID van het ID die op 000 eindigt.
    Last edited by trainlover; 24-Jun-20 at 02:43.

Page 1 of 2 1 2 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •