Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   VirtualStringTree in Thread (https://www.delphipraxis.net/204779-virtualstringtree-thread.html)

Zodi 28. Jun 2020 20:20

VirtualStringTree in Thread
 
Hi Delphianer.

Ich habe folgendes Problem. Ich versuche meine VST in einem Thread zu füllen. Leider funktioniert das füllen nicht mehr
sobald ich es im Thread ausführe.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons,
  VirtualTrees, Vcl.ImgList;

type
  TForm2 = class(TForm)
    ImageList1: TImageList;
    VST: TVirtualStringTree;
    BitBtn1: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
    procedure VSTGetImageIndexEx(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: TImageIndex; var ImageList: TCustomImageList);
    procedure VSTGetNodeDataSize(Sender: TBaseVirtualTree;
    var NodeDataSize: Integer);
    procedure VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  MyThread = class(TThread)
  private
    i:integer;
    { Private-Deklarationen }
  protected
    { protected-Deklarationen }
    procedure Execute; override;
    procedure DoSomething;
  public
    { Public-Deklarationen }
  end;

type
  PTreeData = ^TTreeData;
  TTreeData = packed record
  Column0: String; Column1: String; Column2: String;
end;

var
  MyThr: MyThread;
  Form2: TForm2;

implementation

{$R *.dfm}

procedure MyThread.Execute;
var
  Data: PTreeData;
  XNode: PVirtualNode;
  begin
 while not Terminated do
   begin
     inc(i);

   XNode := form2.VST.AddChild(nil);

   if form2.VST.AbsoluteIndex(XNode) > -1 then
     begin
       Data := form2.VST.GetNodeData(Xnode);
       Data^.Column0 := inttostr(i);
       Data^.Column1 := 'Wert 1';
       Data^.Column2 := 'Wert 2';
     end;

     if i = 10 then begin
        i:=0;
        SuspendThread(MyThr.Handle);
     end;

   Synchronize(DoSomething);
   end;
end;

procedure MyThread.DoSomething;
begin
//
end;

procedure TForm2.BitBtn1Click(Sender: TObject);
begin
       MyThr := MyThread.Create(True);
       MyThr.FreeOnTerminate := true;
       MyThr.Priority := tpIdle;
       ResumeThread(MyThr.Handle);
end;

procedure TForm2.VSTGetImageIndexEx(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: TImageIndex; var ImageList: TCustomImageList);
begin
  if Column = 0 then
    begin
      if Kind in [ikNormal, ikSelected] then ImageIndex := 0;
  end;

end;

procedure TForm2.VSTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
begin
  NodeDataSize := SizeOf(TTreeData);
end;

procedure TForm2.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
  case Column of
    0: CellText := Data^.Column0;
    1: CellText := Data^.Column1;
    2: CellText := Data^.Column2;
  end;
end;

end.
Wenn ich aber im ButtonClick nicht den Tread starte sondern die VST direkt fülle.....
Delphi-Quellcode:
procedure TForm2.BitBtn1Click(Sender: TObject);
var
  Data: PTreeData;
  XNode: PVirtualNode;
begin
   XNode := VST.AddChild(nil);
   Data := VST.GetNodeData(Xnode);

   Data^.Column0 := inttostr(i);
   Data^.Column1 := 'Wert 1';
   Data^.Column2 := 'Wert 2';

end;
dann geht es was mache ich falsch?

Hobbycoder 28. Jun 2020 20:33

AW: VirtualStringTree in Thread
 
Du muss die Daten an den MainThread schicken und das Befüllen des VirtualStringTree auch im MainThread machen.

Aviator 28. Jun 2020 21:53

AW: VirtualStringTree in Thread
 
Du hast eine Methodenaufruf
Delphi-Quellcode:
Synchronize(DoSomething)
im Code. Darin passiert aber gar nichts. Alles, was du mit dem VST machst, muss im Main Thread ausgeführt werden. Sprich, in die
Delphi-Quellcode:
DoSomething()
Methode rein.

Davon ab würde ich aber nur die Daten im Thread sammeln und das Befüllen des VST dem Hauptthread überlassen. Das Befüllen im Thread macht keinen Sinn, da du sowieso ständig mit dem Hauptthread synchronisieren musst.

himitsu 28. Jun 2020 23:18

AW: VirtualStringTree in Thread
 
Ich denke mal hier wird es auch gehn.

Bei der normalen TList kann man die auch im Thread befüllen, wenn man BeginUpdate nicht vergisst und damit den gleichzeigigen Refresh der GUI blockt.

Zodi 1. Jul 2020 22:43

AW: VirtualStringTree in Thread
 
Also ich habe jetzt wie mir oben empfohlen wurde den Befüllteil in's Synchronize geschoben.
Trotzdem funktioniert es nicht. was allerdings noch zu erwähnen ist, ist das ich ein Form1 und ein Form2 Formular habe.
Das Form2 soll mehrmals erzeugt werden.
Dies hab ich so Realisiert.
Delphi-Quellcode:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
 TestForm: TForm2;
begin
 TestForm := TForm2.Create(nil);
 TestForm.Show;
end;
Jedenfals wird so die VST nicht befüllt.

Wenn ich es allerding so mache dann geht es wobei dann nur 1 Formural angezeit wird.
Delphi-Quellcode:
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
 TestForm.Show;
end;
Wo bin ich das auf dem Holzweg?

Aviator 1. Jul 2020 23:20

AW: VirtualStringTree in Thread
 
Das sind etwas wenige Informationen zum Befüllen des VST. Wenn du damit Probleme hast, dann solltest du den SourceCode sinnvollerweise auch hier zeigen. :stupid:

Ich rate jetzt einfach mal: Du gibst deinem Thread eine Referenz auf die Form2 mit. Diese Referenz ist aber die globale Variable im zweiten Formular und dementsprechend nicht richtig "befüllt". Hat also keine gültige Referenz. Du musst deinem Thread entweder die richtige Instanz deiner Form mitgeben oder - in meinen Augen - besser direkt über ein im Thread definiertes Event arbeiten.

Mehr kann ich ohne SourceCode aus meiner :glaskugel: leider nicht herauslesen.

EDIT: Habe mir gerade nochmal den SourceCode aus deinem vorherigen Post angeschaut. Wenn der jetzt noch genau so aussieht, dann wird es wohl genau der Grund sein, den ich oben beschrieben habe, warum es nicht funktioniert.

himitsu 1. Jul 2020 23:22

AW: VirtualStringTree in Thread
 
Na klar, du verwendest in dem Thread ja auch die globale Variable form2.
Es wird also immer nur in dieser Form der Tree gefüllt.

Übergib dem Thread die gewünscht Form-Instanz und verwende sie in dem Thread.

Tipp: Wenn du die Form2 selbst erstellst, dann schmeiß die automatisch erzeugte Form-Instant ganz weg, am Besten auch gleich diese globale Variable löschen.
Dann siehst'e wie schön es in deinem Thread knallt, wenn nicht mehr auf die globale Variable zugegriffen werden kann.

Zodi 1. Jul 2020 23:51

AW: VirtualStringTree in Thread
 
Meinst du dem Thread die Form.Handle übergeben?


Zitat:

Zitat von himitsu (Beitrag 1468694)
Na klar, du verwendest in dem Thread ja auch die globale Variable form2.
Es wird also immer nur in dieser Form der Tree gefüllt.

Übergib dem Thread die gewünscht Form-Instanz und verwende sie in dem Thread.

Tipp: Wenn du die Form2 selbst erstellst, dann schmeiß die automatisch erzeugte Form-Instant ganz weg, am Besten auch gleich diese globale Variable löschen.
Dann siehst'e wie schön es in deinem Thread knallt, wenn nicht mehr auf die globale Variable zugegriffen werden kann.


Hobbycoder 2. Jul 2020 09:19

AW: VirtualStringTree in Thread
 
Wenn du per synchronize eine Methode des MainThreads ausführst, in der du dann die Daten direkt in die VST füllt, läuft diese Methode aber trotzdem im Thread und VST quittiert das mit einer passenden Meldung, dass das aus einem Thread nicht geht.
Du solltest die Daten per Synchronize an eine Datenstruktur im MainThread, auch über synchronize, übergeben, und anschließen schickst per PostMessage eine Msg an den Hauptthread. Nun kann der Hauptthread ein seinem eigenen Kontext das VST befüllen.

Hier mal aus dem Kopf, wie das ablaufen kann:

MainForm:
Delphi-Quellcode:
inferface

const
  WM_UPDATEVST = WM_USER + 1;
.
.
.
private
  LocalData: TDatenStruktur;
  procedure BuildVSTData;
public
  procedure UpdateVSTData(msg: TMessage); message WM_UPDATEVST;
  procedure UpdateDataFromThread(ThreadData: TDatenStruktur);
end;
.
.
.
implementation

procedure TMainForm.BuildVSTData;
begin
  //was auch immer du machst um deine Daten in die VST zu kriegen
end;

procedure TMainForm.UpdateVSTData(msg: TMessage);
begin
  BuildVSTData;
end;

procedure UpdateDataFromThread(ThreadData: TDatenStruktur);
begin
  LocalData.Assign(ThreadData); //Daten übertragen aus dem Thread in die Daten der MainForm
end;
Thread: (Im Create musst du das Handle der MainForm übergeben).
Delphi-Quellcode:
procedure Execute;
var
  FThreaData: TDatenStruktur;
begin
  FThreadData:=TDatenStruktur.Create;
  try
    //Daten sammeln
    synchronize(procdure
      begin
        frmMainForm.UpdateDataFromThread(FThreadData);
      end);
    PostMessage(MainThreadHandle, WM_UPDATEVST, 0, 0);
  finally
    FThreadData.Free;
  end;
end;
Ich habe das hier so einfach reingeklöppelt (bin grad nicht am Rechner). Wird also so nicht AdHoc funktionieren. Aber wie ich das meine wird schon deutlich.
So kannst du das machen. Die Daten übergibst du im Threadcontext, aber das befüllen der VST läuft dann im MainFormContext.

Man muss das natürlich noch einiges verfeinern.

Zodi 2. Jul 2020 19:27

AW: VirtualStringTree in Thread
 
Liste der Anhänge anzeigen (Anzahl: 1)
Es geht leider immer noch nicht.

Irgendwie weis das VST oder der Thread nicht in Welche Form er schreiben soll.
Hab hier mal meinen Code in ne .rar angehängt.

Aviator 2. Jul 2020 21:11

AW: VirtualStringTree in Thread
 
Ich habe kein Delphi zur Hand um dir eine funktionsfähige Lösung zu bauen. Aber setz doch einfach mal die Tipps um die wir dir gegeben haben. Der erste Schritt mit dem du anfangen solltest wäre, dass du in Unit2.pas in Zeile 54 die
Delphi-Quellcode:
Form2: TForm2;
Deklaration entfernst. Genau das ist nämlich dein Problem. Diese globale Variable hat einen falschen bzw. keinen Wert, da du in Form1 die Form2 Instanz dynamisch anlegst.

Wenn du die Deklaration jetzt entfernt hast, dann wirst du automatisch feststellen, dass in Zeile 84 ein Fehler erscheint. Und diesen Fehler musst du beseitigen ... eben mit einer der vielen Lösungen die wir die vorgeschlagen haben.

Die sinnvollste Variante ist allerdings die, dass du einfach nur im Thread die Daten sammelst und diese an die Form2 Instanz übergibst. Den VST lässt du dann von der Form aufbauen. Der Thread sollte damit nichts zu tun haben.

jus 2. Jul 2020 22:34

AW: VirtualStringTree in Thread
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Zodi,
ich habe mal dein Beispiel einbisschen abgeändert. Ich hoffe es kommt so ungefähr in die Richtung, wie du es haben willst. Ich habe das Gefühl, dass du dir das Thema Thread Programmierung mal genauer anschauen solltest. Weiters hoffe ich, dass es dir klar ist, dass eine Instanz von Form2 gleich beim Programmstart erzeugt wird. Das kannst du definitiv in den Projektoptionen "Formulare" unterbinden, indem du Form2 in die Verfügbare Formulare rüberschiebst.

lg,
jus


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:45 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz