Auf Data von per Assign kopierten TreeView.Items zugreifen

Für Fragen von Einsteigern und Programmieranfängern...
cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Auf Data von per Assign kopierten TreeView.Items zugreifen

Beitrag von cle »

Moin,

ich brauche zwei synchronisierte TreeViews. Dazu kopiere ich den Inhalt des ersten per Items.Assign in den zweiten, was soweit auch funktioniert. Laut Doku sollen dabei auch die mit den Items verknüpften Objekte 'kopiert' werden. Allerdings erhalte ich einen SIGSEGV, wenn ich auf die Daten des zweiten zugreifen will.
Zur Verdeutlichung erstelle ich eine Beispielklasse, hänge eine erstellte Instanz an einen Knoten im TreeView1 und lasse mir im onChange Ereigniss eine Info in einem Label anzeigen. Das funktioniert, wenn ich einen Eintrag im TreeView1 auswähle, im zweiten nicht.
Offensichtlich mache ich etwas falsch!?

Gruß
Alex

Code: Alles auswählen

 
unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  ComCtrls, StdCtrls;
 
type
 
  TTestClass = class(TObject)
    private
      FText: String;
    public
      constructor Create;
      destructor Destroy; override;
      procedure ShowInfo;
  end;
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Label1: TLabel;
    TreeView1: TTreeView;
    TreeView2: TTreeView;
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure TreeView2Change(Sender: TObject; Node: TTreeNode);
  private
    { private declarations }
  public
    { public declarations }
  end;
 
 
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  tc: TTestClass;
begin
  for i := 0 to 9 do
  begin
    tc := TTestClass.Create;
    tc.FText := 'Node Info Nr.' + IntToStr(i);
    TreeView1.Items.AddObject(TreeView1.Items.GetLastNode, 'node' + inttostr(i), tc);
  end;
  TreeView2.Items.Assign(TreeView1.Items);
end;
 
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
  i: Integer;
begin
  for i := 0 to TreeView1.Items.Count-1 do
  begin
    TTestClass(TreeView1.Items[i].Data).Free;
  end;
end;
 
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
  if Assigned(Node.Data) then TTestClass(Node.Data).ShowInfo;
end;
 
procedure TForm1.TreeView2Change(Sender: TObject; Node: TTreeNode);
begin
  if Assigned(Node.Data) then TTestClass(Node.Data).ShowInfo;   // ********* Hier rumpelts
end;
 
 
constructor TTestClass.Create;
begin
  inherited Create;
end;
 
destructor TTestClass.Destroy;
begin
  inherited Destroy;
end;
 
procedure TTestClass.ShowInfo;
begin
  Form1.Label1.Caption := FText;
end;
 
end.
 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

Ich habe dein Beispiel eben unter Lazarus Trunk probiert, da geht es. Eben noch unter Lazarus 1.8.5, da geht es nicht. Der Bug ist daher im nächsten Major Release behoben. Bis dahin könntest du Lazarus Trunk nutzen oder müsstest nachträglich händisch die Objekte verknüpfen.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

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

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Ich glaube nicht, dass es auch unter Trunk ein sauberes Programm ergibt:

Die "Objekte", die im Code per AddObject an den TreeNode hängt, sind im allgemeinen Fall nur Pointer.

Code: Alles auswählen

 // TTreeNodes:
     function AddObject(SiblingNode: TTreeNode; const S: string; Data: Pointer): TTreeNode;

Das heißt, das Assign "weiß" gar nicht, wieviele Daten zu kopieren sind. Und in TTreeNode.Assign von Laz-Trunk sieht man tatsächlich, dass nur der Pointer kopiert wird:

Code: Alles auswählen

procedure TTreeNode.Assign(Source: TPersistent);
var
  ANode: TTreeNode;
begin
  if Owner<>nil then Owner.ClearCache;
  if Source is TTreeNode then
  begin
    ANode := TTreeNode(Source);
    Text := ANode.Text;
    Data := ANode.Data;   // <---- HIER
 ...
  end
  else inherited Assign(Source);
end;

Hat der 1.Tree die Nodes sauber aufräumt und dabei die eingehängten Objekte zerstört, kracht es, wenn anschließend der 2.Tree dasselbe macht, weil die eingehängten Objekte nicht mehr existieren - die gab es ja nur 1x.

Besser wäre es, alle Daten außerhalb der TreeViews zu speichern, so dass es sie nur 1x gibt; die beiden Trees müssten sich dann die Daten von der zentralen Stelle abholen und nur anzeigen. TTreeView kann das nicht, aber mit VirtualTreeView sollte es funktionieren.

Oder du stellst sicher dass der 2.Tree die in die Nodes eingehängten Objekte nicht zerstören kann. Erscheint mir allerdings eine Zeitbombe zu sein...

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von af0815 »

Irgendwie passt das mir jetzt das vom Verhalten bei Assign nicht zusammen.

Wenn ich mit Assign einem 2ten Objekt etwas zuweise, dann erwarte ich mir das ich es auch wieder zerstören kann ohne Auswirkung auf das andere Objekt. Entweder dürfen die Objecte nicht zerstört werden oder sind nur referenzgezählte Objekte oder müssen kopiert werden.

Weil wer ist nach einem Assign wirklich für die Verwaltung der Objekte zuständig ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

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

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Genau das versuchte ich auszudrücken. Ich finde, das ist eine üble Falle.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

af0815 hat geschrieben:Weil wer ist nach einem Assign wirklich für die Verwaltung der Objekte zuständig ?
Siehe Beispiel von oben. Dort wo etwas erstellt wird, muss es auch wieder freigegeben werden. Ein TreeView macht beim Zerstören nichts mit den Objekten. Ist doch dann alles gut so.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

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

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Richtig. Da war mein Beitrag irreführend...

Trotzdem: das Unbehagen bleibt bestehen: Da greifen zwei TreeViews auf dieselben Data-Objekte zu. Letztere werden zerstört, und keiner kriegt's mit. Daher würde ich zumindest nach dem TObject(Node.Data).Free noch ein Node.Data := nil einfügen, und nochmals ein TreeView2.Items.Assign(TreeView1.Items), damit die NIL-Data auch im 2.Tree eingetragen sind. Denn nach Form.OnClose kann noch eine Menge passieren, schließlich wird dort das Formular nur versteckt. Oder meinst du OnDestroy? Dort könntest du wahrscheinlich auf diese Vorsichtsmaßnahmen verzichten.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

wp_xyz hat geschrieben:Denn nach Form.OnClose kann noch eine Menge passieren, schließlich wird dort das Formular nur versteckt. Oder meinst du OnDestroy? Dort könntest du wahrscheinlich auf diese Vorsichtsmaßnahmen verzichten.
Richtig zu OnCreate gehört OnDestroy und müsste oben geändert werden.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von cle »

Oh, da habe ich eine kleine Diskussion ausgelöst ;)
Danke jedenfalls für eure Antworten.

Trunk habe ich benutzt (vergessen zu erwähnen), war aber vielleicht ein paar Tage zu alt. Werde ich nochmal testen.
Dass die Verwaltung der Daten heikel ist, ist mir bewusst. Da muss ich schon selbst für sorgen.
Dass bei onClose das Formular nur versteckt wird, habe ich nicht gewusst. Also sollte man Aufräumprozesse in onDestroy durchführen? Woher weiß ich denn, dass die entspr. Komponenten dann überhaupt noch existieren?

Gruß
Alex

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

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Wie Michl schon sagte, ist OnDestroy das Gegenstück zu OnCreate, es wird in umgekehrter Reihenfolge das alles aufgeräumt, was in OnCreate erzeugt wurde. Wenn es richtig gemacht ist, muss alles noch da sein: zuerst kommt das OnDestroy-Ereignis, dann gibt das Formular seine Untercontrols frei und dann sich selbst. Wenn's nicht so gut gemacht ist, muss man in der Routine, in der es evtl kracht, den Zustand abfragen:

Code: Alles auswählen

  if not (csDestroying in ComponentState) then... 

Wenn dir OnDestroy zu knapp vor dem Ende ist, kannst du auch bei OnClose bleiben, solltest aber den Parameter Action auf caFree setzen, damit sich das Formular freigibt und nicht nur verbirgt.

cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von cle »

Okay, das ist interessant. Ich dachte immer, onClose wäre der Hinweis, alles in Sicherheit zu bringen und aufzuräumen.
Vielen Dank für die Erklärung.

cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von cle »

Ich habe vor zwei Tagen mit fpcupdeluxe Lazarus Trunk aktualisiert (Linux, 64, gtk2) und damit stürzt das Programm von oben wie gehabt ab. Mit Windows hat fpcupdeluxe nicht funktioniert, bei einem Laz 1.8.2 hat immerhin das if Assigned(Node.Data) gegriffen, im Gegensatz zu Linux.
Nur zur nachträglichen Info @Michl

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

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Ich fürchte, der Fehler liegt wo anders. Du solltest ein kleines Demo-Programm schreiben, in dem der Fehler sichtbar wird und mit dem wir hier uns das näher ansehen können. Ich weiß nicht, ob man hier schon wieder Anhänge posten kann. Wenn ja, dann schnüre pas, lfm, lpi and lpr-Dateien (sowie evtl Daten-Dateien) zu einem zip zusammmen und lade dieses hier hoch. Wenn nicht, dann speichere das Demo-Programm irgendwo in einer Cloud.

cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von cle »

Ähm, aber das Beispiel steht doch schon im ersten Post, incl. Erklärung wann es crashed ...

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

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Schon, aber das kann ich nicht kompilieren, weil das meiste fehlt: Wo ist die lfm-Datei, in der falsche Properties stecken können, wo die lpi-Datei mit evtl. falschen Compilereinstellungen usw.

Antworten