VirtualStringTree und Array

Rund um die LCL und andere Komponenten
Antworten
Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

VirtualStringTree und Array

Beitrag von Ronny58 »

Hallo Zusammen,

was ist da falsch? TreeView zeigt mir die Header an, aber nicht die Nodes.
Ich fülle eine Treeview aus einer CSV-Datei mit unbekannter Anzahl Spalten. Daher in der Type-Definition ein Array.
Die Header baue ich auch zur Laufzeit - das klappt.
Dann lade ich die Nodes - RootNodeCount = 1145 nach dem Laden. Nodes werden aber nicht angezeigt.
Ersetze ich das Array durch einen String und lese nur die erste Spalte aus der CSV ein, erhalte ich eine Anzeige für jeden Node.
Ich hab schon erheblich komplexerer Strukturen im TreeView erfolgreich verarbeitet.
Aber hier sehe ich den Fehler einfach nicht. Hab's schon einen Tag liegen lassen. Manchmal muss einfach mal eine Pause machen um den eigenen Fehler zu sehen. Ohne Erfolg.

Code: Alles auswählen

type

  TImportColumn = Array[0..100] of string;
  TImportData = record
     Spalte : TImportColumn;
  end;
  PImportData = ^TImportData;


procedure TForm1.vstImportGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
begin
  vstImport.NodeDataSize:=SizeOf(TImportData);
end;

procedure TForm1.vstImportGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
var Data: PImportData;
begin
  Data := vstImport.GetNodeData(Node);
  CellText := Data^.Spalte[Column];
end;

procedure TForm1.FormShow(Sender: TObject);
var sZeile : String;
    iCol, iZeile : Integer;
    vtc : TVirtualTreeColumn;
    xNode : PVirtualNode;
    Data: PImportData;
    tslCSV : TStringList;
begin
  tslCSV.LoadFromFile('C:\Applikationen\TM1_Strukturen\Kostenarten.csv');
  vstImport.Clear;
  vstImport.Header.Columns.Clear;
  sZeile := ConvertEncoding(tslCSV.Strings[0],GuessEncoding(tslCSV.Strings[0]), EncodingUTF8);
  iColCount := ParseCount(';',sZeile);
  for iCol := 1 to iColCount do
  begin    
    s := Parse(';',sZeile,iCol);
    vtc := vstImport.Header.Columns.Add;	
    vstImport.Header.Columns[iCol-1].Style:=vsText;
    vstImport.Header.Columns[iCol-1].Options := [coAllowClick,coDraggable,coEnabled,coParentBidiMode,coResizable,coShowDropMark,coVisible,coAllowFocus,coEditable];
    vstImport.Header.Columns[iCol-1].Width:=100;
    vstImport.Header.Columns[iCol-1].Text:=s;
  end;
// füllen des Treeview aus der TStringlist
  for iZeile := 1 to tslCSV.Count-1 do
  begin
    sZeile := ConvertEncoding(tslCSV.Strings[iZeile],GuessEncoding(tslCSV.Strings[iZeile]), EncodingUTF8);
    XNode  := vstImport.AddChild(nil);
    Data := vstImport.GetNodeData(XNode);
    fillchar(data^,sizeof(data^),0);
    for iCol := 1 to iColCount do
       		Data^.Spalte[iCol-1] :=Parse(';',sZeile,iCol);
  end;
end;


Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 688
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: VirtualStringTree und Array

Beitrag von fliegermichl »

Hallo,

Tree.Header.Options.hoVisible muß auf true gesetzt werden.

Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

Re: VirtualStringTree und Array

Beitrag von Ronny58 »

Die Option ist gesetzt.
Nebenbei. Setze ich einen Breakpoint in GetText, kommt er dort nie an

Mache ich folgendes

Code: Alles auswählen

  TImportData = record
     Spalte : String;
  end;
und beim Import

Code: Alles auswählen

Data^.Spalte :=Parse(';',sZeile,1);
und im GetText

Code: Alles auswählen

  Data := vstImport.GetNodeData(Node);
  if Column = 0 then CellText := Data^.Spalte;
Dann zeigt er mir in der ersten Spalte den richtigen Inhalt an.

Irgendwas ist falsch wenn die Typ-Definition ein Array ist

Code: Alles auswählen

    Data := vstImport.GetNodeData(XNode);
    for iCol := 1 to iColCount do
        Data^.Spalte[iCol-1] :=Parse(';',sZeile,iCol);
und bei GetText

Code: Alles auswählen

  Data := vstImport.GetNodeData(Node);
  CellText := Data^.Spalte[Column];

wp_xyz
Beiträge: 3359
Registriert: Fr 8. Apr 2011, 09:01

Re: VirtualStringTree und Array

Beitrag von wp_xyz »

Scheint alles richtig zu sein. Könntest du ein kleines Projekt zusammenklicken, in dem das Problem vereinfacht auftritt?

Ich glaube, es fehlt noch ein Handler für OnFreeNode, in dem du für jeden Node die Strings freigibst:

Code: Alles auswählen

procedure TForm1.LazVirtualStringTree1FreeNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  data: PImportData;
begin
  data := Sender.GetNodeData(Node);
  for i := 0 to High(data^.Spalte) do
    data^.Spalte[i] := '';
end;

wp_xyz
Beiträge: 3359
Registriert: Fr 8. Apr 2011, 09:01

Re: VirtualStringTree und Array

Beitrag von wp_xyz »

Es fällt mir auf dass tslCSV eine lokale StringList in FormShow ist, aber dort nicht erzeugt wird. Eigentlich sollte das Programm abstürzen.

wp_xyz
Beiträge: 3359
Registriert: Fr 8. Apr 2011, 09:01

Re: VirtualStringTree und Array

Beitrag von wp_xyz »

Jetzt habe ich mir selbst ein kleines Projekt aus deinem Code gemacht und kann bestätigen, dass der VTV leer bleibt. Habe keine Ahnung, warum ein fest dimensioniertes Array[] of string im Node-Record nicht funktioniert.

Aber ehrlich, warum machst du das? Ein Array fest zu dimensionieren, nach dem Motto "Nehmen wir 100 - wird schon reichen...", ist Technologie von Anfänger-Turbo-Pascal. Heute gibt es dynamische Arrays, die man zur Laufzeit per SetLength so dimensionieren kann, dass kein Byte zu viel und zu wenig ist. Es gibt sogar den fertigen Typen TStringArray = array of string, der der Ergebnistype des String-Helpers .Split() ist, mit dem du die ganze CSV-Behandlung geschenkt kriegst.

Schau dir mein Beispiel an; es funktioniert mit dem TStringArray.
Dateianhänge
forum_vstImport.zip
(2.83 KiB) 18-mal heruntergeladen

Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

Re: VirtualStringTree und Array

Beitrag von Ronny58 »

Vielen Dank für den Tipp. Du hast mir schon sehr oft geholfen. Tausend Dank dafür.

Dein Code "separates the man from the boys". An TStringArray habe ich gar nicht gedacht. Ich bin zwar etwas erfahren aber kein Profi.
Ich nutze Pascal gelegentlich für Problemlösungen die bei teuren teuren Kauf-Applikationen auftreten, weil diese es nicht ordentlich können.
In diesem Fall die Pflege von Hierarchien inkl. Attributen zu einer Kostenartstruktur in IBM-TM1. Im Prinzip aber fast alle Hierarchien in der Datenbank, da die Vorsysteme diese Informationen nicht in vernünftigen Strukturen liefern können und die Schnittstelle dazu in TM1 grottig ist. Ich administriere diese Datenbank.
Hab da auch schon ein Tool zur Verwaltung vom TM1-Server-Prozessen geschrieben. Das was IBM da anbietet ist minimal-Basic, also eigentlich nicht "real-existierend".

Trotzdem musste ich Deinen Code nochmal anpassen.

1. Die Erzeugung der Header.
Dein Code hat bei mir keine Spaltenüberschriften erzeugt. Wenn ich aber erst die Spalten erzeuge und in einer zweiten Schleife gestalte, dann klappt es. Eigentlich sollte es nicht so sein.

Code: Alles auswählen

    for iCol := 0 to iColCount-1 do vtc := vstImport.Header.Columns.Add;
    for iCol := 0 to iColCount-1 do
    begin
      vstImport.Header.Columns[iCol].Style:=vsText;
      vstImport.Header.Columns[iCol].Options := [coAllowClick,coDraggable,coEnabled,coParentBidiMode,coResizable,coShowDropMark,coVisible,coAllowFocus,coEditable];
      vstImport.Header.Columns[iCol].Width:=100;
      vstImport.Header.Columns[iCol].Text := sa[iCol];
    end;
2. Der Split bei den Daten hat auch zu einem Fehler geführt. Warum auch immer lief der bei mir im OnGetText-Ereignis auf Array-Fehler.

Code: Alles auswählen

      Data^.Spalte := sZeile.Split(';');
ich habe es dann so gemacht:

Code: Alles auswählen

      SetLength(Data^.Spalte, iColCount);
      for iCol := 0 to iColCount -1 do
          Data^.Spalte[iCol] := parse(';',sZeile,iCol+1)
So hat es dann geklappt. Parse ist eine kleine Funktion, die ich mir selber geschrieben habe und liefert bei zwei aufeinander folgenden Semikolons einen Leerstring.
Vielleicht hat der Split mit Leerspalten ein Problem (;;). Die kommen bei meinen Attributen durchaus vor. (Lazarus-Version 2.0.6 vom 30.03.2020)
Warum Dein Code nicht komplett funktioniert hat will mir nicht in den Kopf. Weil nach meinem Verständnis hätte er es müssen.
Aber ich bin überglücklich, dass ich nun eine Lösung habe. Und die TStringArray-Lösung gefällt mir auch besser als mein statisches Array. Jetzt kann ich mich an die Prüfung der CSV machen, ob die den Konventionen meines Programm entspricht.

Mein Problem ist gelöst.

wp_xyz
Beiträge: 3359
Registriert: Fr 8. Apr 2011, 09:01

Re: VirtualStringTree und Array

Beitrag von wp_xyz »

Ronny58 hat geschrieben:
Mo 31. Aug 2020, 10:31
Dein Code hat bei mir keine Spaltenüberschriften erzeugt. Wenn ich aber erst die Spalten erzeuge und in einer zweiten Schleife gestalte, dann klappt es. Eigentlich sollte es nicht so sein.

Code: Alles auswählen

    for iCol := 0 to iColCount-1 do vtc := vstImport.Header.Columns.Add;
    for iCol := 0 to iColCount-1 do
    begin
      vstImport.Header.Columns[iCol].Style:=vsText;
      vstImport.Header.Columns[iCol].Options := [coAllowClick,coDraggable,coEnabled,coParentBidiMode,coResizable,coShowDropMark,coVisible,coAllowFocus,coEditable];
      vstImport.Header.Columns[iCol].Width:=100;
      vstImport.Header.Columns[iCol].Text := sa[iCol];
    end;
Seltsam. Was ist dein Betriebssystem, Laz-Version, FPC-Version?
Ronny58 hat geschrieben:
Mo 31. Aug 2020, 10:31
2. Der Split bei den Daten hat auch zu einem Fehler geführt. Warum auch immer lief der bei mir im OnGetText-Ereignis auf Array-Fehler.

Code: Alles auswählen

      Data^.Spalte := sZeile.Split(';');
ich habe es dann so gemacht:

Code: Alles auswählen

      SetLength(Data^.Spalte, iColCount);
      for iCol := 0 to iColCount -1 do
          Data^.Spalte[iCol] := parse(';',sZeile,iCol+1)
Auch seltsam. Vielleicht ist das CSV etwas complizierter als von .Split() angenommen? Könntest du mal einen typischen String sZeile posten, bei dem Data^.Spalte := sZeile.Split(';') abstürzt? Oder noch besser: Poste gleich die ganze CSV-Datei (wahrscheinlich musst du zippen); wenn du die nicht veröffentlichen darfst, laden sie auf eine Cloud und schicke mir den Link per PM.

Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

Re: VirtualStringTree und Array

Beitrag von Ronny58 »

Windows 10, Lazarus-Version 2.0.6 vom 30.03.2020.
Das mit der Datei geht nicht so schnell. Muss mir von der IT erst ein Sharefile freischalten lassen.
Kostenartenstruktur ist zwar nicht so ganz Top-Secret, wäre aber doch irgendwie blöd die im Internet zu finden :)

Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

Re: VirtualStringTree und Array

Beitrag von Ronny58 »

Hallo wp_xyz,

ich habe nun eine CSV-Datei mit verfälschtem Inhalt erstellt und diesem Post angehängt (Vater,Kind,Schlüssel).
Ich habe diese Datei mit Deiner Lösung getestet. Es kommt das gleich Ergebnis dabei raus.
Wenn man sich beim Spaltenaufbau in Inhalt von sa[iCol] ansieht, ist dieser plausibel. Dem vtc wird dieser Wert aber nicht zugeordnet.
Mit zwei Schleifen (die 1. erstellt die Spalte; die 2. definiert sie) klappt es. In einer Schleifen nicht.
Der Datenbereich verhält sich völlig eigenartig mit dem Data^.Spalte := sZeile.Split(';')
Das Verhalten lässt sich gut darstellen.

Hier nochmal meine eigenen Funktionen zum zerlegen von CSV-Dateien

Code: Alles auswählen

  Function ParseCount(Char, S: string): Integer;
  var i,  iRes : Integer;
  begin
    result := 0;
    iRes  := 0;
    if length(s) = 0 then exit;
    for i := 1 to length(s) do
    begin
      if s[i] = Char then inc(iRes);
    end;
    if s[i] <> Char then inc(iRes);
    ParseCount := iRes;
  end;

  function Parse(Char, S: string; Count: Integer): string;
    var
     I: Integer;
     T: string;
   begin
     result := '';
     if length(s) = 0 then exit;
     if S[Length(S)] <> Char then
       S := S + Char;
     for I := 1 to Count do
     begin
       TRY
         T := Copy(S, 0, Pos(Char, S) - 1);
         S := Copy(S, Pos(Char, S) + 1, Length(S));
       except
         exit;
       end;
     end;
     Result := T;
  end;
LG Ronny
Dateianhänge
Kostenart_Test.7z
(41.54 KiB) 35-mal heruntergeladen

wp_xyz
Beiträge: 3359
Registriert: Fr 8. Apr 2011, 09:01

Re: VirtualStringTree und Array

Beitrag von wp_xyz »

Ich habe nun meine eigene csv-Datei durch deine Kostenart_test.csv ersetzt, sehe aber weder mit Laz-trunk noch mit dem von dir verwendeten Laz 2.06 das beschriebene Problem (weder mit 32-bit noch mit 64 bit).

Welches VirtualTreeView-Package verwendest du denn? Es gibt da einige Varianten im Internet... Ich nehme bei Laz-Trunk die bereits eingebaute Version, und bei älteren Lazarussen die Version aus dem Online-Package-Manager.

Wie ich eben gesehen habe, müsstest du eigentlich mit Laz 2.0.6 Probleme beim Laden des oben geposteten Demo-Projekts gehabt haben, denn es setzt die mit Lazarus ausgelieferte Version von VTV mit dem Namen laz.VirtualTreeviews-5.5.3.1 voraus, das es bei 2.06. gar nicht gibt. Ich lade das Programm nochmals hoch, nun speziell für Laz-vor-Trunk und mit deiner Kostenarten-Datei.
Dateianhänge
forum_vstImport - v206.zip
(46.74 KiB) 9-mal heruntergeladen

Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

Re: VirtualStringTree und Array

Beitrag von Ronny58 »

Ja, das stimmt. Ich hatte das Problem mit dem Treeview. Hatte mich schon gewundert, warum bei Dir ein laz. davor steht.
Bei den installierten Packages steht bei Virtualtreeview_package die Version 5.5.3.1.

Ich habe eine Trunk-Version und auch den Online-Packagemanager installiert.
Die Version die ich einsetze habe ich mittels TorToiseSVN über den Link "https://svn.freepascal.org/svn/lazarus/trunk" gezogen.

Ist das keine gute Quelle? Wenn ja, wo sollte ich mich bedienen?

wp_xyz
Beiträge: 3359
Registriert: Fr 8. Apr 2011, 09:01

Re: VirtualStringTree und Array

Beitrag von wp_xyz »

Sollte passen. Dann weiß ich keinen Grund mehr, warum sich mein Programm bei dir anders verhält als bei mir.

Ronny58
Beiträge: 73
Registriert: So 27. Apr 2014, 20:35

Re: VirtualStringTree und Array

Beitrag von Ronny58 »

Vielen Dank nochmal für Deine Antworten auf meine Fragen.
Auch wenn ich Deinen Code nicht 1:1 übernehmen konnte, hat der Grundsätzlich Lösungsansatz über TStringArray ganz hervorragend mein Problem gelöst.
Mittlerweile habe ich das Tool im Einsatz. Mit Spaltenvalidierung, Inhaltsvalidierung, gezielten Fehlermeldungen, wenn etwas nicht passt.
Sollte der Ersteller der CSV-Datei sich nicht an die Vorgaben meines Programmes gehalten haben, können Spalten per Drag und Drop an die richtige Position gezogen werden. Richtig Schick dank Deinem TIP.
Jetzt fehlt mir noch der Dialog zur Pflege von Attributsklassen und Attributen. Da bin ich gerade dran. Dann kann ich sage "habe fertig".

Antworten