AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi GroupBox auf Mainform => Repaint in Thread = Kill
Thema durchsuchen
Ansicht
Themen-Optionen

GroupBox auf Mainform => Repaint in Thread = Kill

Ein Thema von xZise · begonnen am 13. Jul 2007 · letzter Beitrag vom 13. Jul 2007
Antwort Antwort
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#1

GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 13:30
Hi DP,
ich habe eine Groupbox auf der Mainform, welche in in einen Object erstellt habe.

Nun repainte ich diese Box in einen Syncronized Prozedur in einen Thread des Objectes.
Das ergebnis ist, dass das Programm einfriert.

Aber nicht nur beim Repaint passiert das.
Sondern schon dann, wenn ich versuche Labels die Box als Parant zu setzen.
Bei der ProgressBar klappt es seltsamerweise, aber wenn ich daran die Position ändere hängt es sich wieder auf.
Aber auch nur dann, wenn der Parent der Bar die Box ist...

Nun, warum friert es ein?

MfG
xZise
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
Benutzerbild von Gausi
Gausi

Registriert seit: 17. Jul 2005
880 Beiträge
 
Delphi 11 Alexandria
 
#2

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 13:44
VCL-Zugriffe in einem Thread sind böse. VCL-Objekte in einem Thread erstellen auch. Alles, was mit VCL zu tun hat, muss im VCL-Thread laufen, d.h. von einem anderen Thread aus nur über Synchronize.
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#3

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 13:50
OOPs... Ich hatte mich vertan sry Und zwar wird das doch nicht im Thread erstellt...

Ach am besten hier mal der Code:

Delphi-Quellcode:
Unit FileReceiver;

Interface

uses
  Classes, SysUtils, stdctrls, comctrls, IdTCPServer, FileCtrl, Windows;

type

  TFileReceiver = class
  private
    fServerMsgOk: Boolean;
    // Optische Elemente zur Darstellung des Threads
    fGB: TGroupBox;
    fpBar: TProgressBar;
    fLabFS: TLabel;
    fLabRe: TLabel;
    // Indy-Server-Thread
    AThread: TIdPeerThread;
    // wichtige Elemente zum Empfangen der Datei
    iFileSize: Cardinal;
    iReceivedBytes: Cardinal;
    sFileName: String;
    iDownloadRate : Cardinal;
    tmpMS: TMemoryStream;
    Procedure CreateElements;
    Procedure DestroyElements;
    Function VBSplit(Liste: TStringList; Text2Split: String; SeperatorStr: String): Boolean;
    Procedure UpdateProgress;
  protected
    //
  public
    Constructor Create(Thread: TIdPeerThread; Msg: String);
    Destructor Free;

    Property ServerMsgOK: Boolean read fServerMsgOk write fServerMsgOk;
    Property Filename: String read sFileName;
    Function Start(Path_to_Save_in: String): Boolean;
  End;

Implementation

Uses f_Main, Controls;

{ TFileReceiver }

Constructor TFileReceiver.Create(Thread: TIdPeerThread; Msg: String);
Var
  strL: TStringList;
Begin
  fServerMsgOk := false;
  AThread := Thread;
  If AThread = Nil Then
    exit;
  // Zwischenspeicher zum empfangen der Pakete erzeugen
  tmpMS := TMemoryStream.Create;
  // Nachricht vom Client splitten
  strL := TStringList.create;
  Try
    VBSplit(strL, Msg, '|');
    // eine gültige Client-Nachricht besteht aus zwei Teilen
    If strL.Count = 2 Then Begin
      // zweites Elemt die Gesamtdateigrösse
      iFileSize := StrToIntDef(strL[0], 0);
      fLabFS.Caption := Inttostr(iFileSize) + ' Bytes';
      // drittes Element enthält den Filenamen
      sFileName := strL[1];
      //prüfen, ob gültige Werte übertragen wurden
      fServerMsgOk := ((iFileSize > 0) And (Length(sFileName) > 0));
    End;
  Finally
    strL.free;
  End;
  CreateElements;
End;

Destructor TFileReceiver.Free;
Begin
  tmpMS.Clear;
  FreeAndNil(tmpMS);
  DestroyElements;
End;

Procedure TFileReceiver.CreateElements;
Begin
  // GroupBox erzeigen
  fGB := TGroupBox.Create(frmMainServer.ScrollBox1);
  fGB.Parent := frmMainServer.ScrollBox1;
  fGB.Height := 57;
  fGB.Align := alTop;
  fGB.Caption := 'Client(' + AThread.Connection.Socket.Binding.PeerIP + ') überträgt ' + sFileName;
  fGB.Visible := true;

  // Progressbar erzeugen
  fpBar := TProgressBar.Create(fGB);
  //fpBar.Parent := fGB;
  fpBar.Left := 8;
  fpBar.Top := 24;
  fpBar.Width := 594;
  fpBar.Anchors := [akLeft, akTop, akRight];
  fpBar.Visible := true;

  // Labels erzeugen
  fLabFS := TLabel.create(fGB);
  //fLabFS.Parent := fGB;
  fLabFS.Left := 3;
  fLabFS.top := 57;
  fLabFS.Anchors := [akTop, akLeft];
  fLabFS.Caption := 'Filesize: 0 KB';
  fLabFS.Visible := true;

  fLabRe := TLabel.create(fGB);
  //fLabRe.Parent := fGB;
  fLabRe.Left := 3;
  fLabRe.top := 38;
  fLabRe.Anchors := [akTop, akLeft];
  fLabRe.Caption := 'Received Bytes: 0 KB (0 %) @ 0 kbit/s';
  fLabRe.Visible := true;

  //fgb.Repaint;
  frmMainServer.ScrollBox1.Repaint;
End;

Procedure TFileReceiver.DestroyElements;
Begin
  // hier nur die Groupbox freigeben, alle anderen Controls nicht Childs der GroupBox
  // und werden somit mit freigegeben
  fGB.free;
End;

// ********* VBSplit ***********************************************************
// Author 23.3.2001 J. Freese alias DataCool
// Function Splits a string in different substring speraded by SeperatorStr
// Param List where the substrings were added
// Text2Split string which should be splitted
// SeperatorStr String which are used as Seperator
// Return true if success

Function TFileReceiver.VBSplit(Liste: TStringList; Text2Split, SeperatorStr: String): Boolean;
Var
  Posi: Longint;
  strTemp: String;
  strPart: String;
  bInLoop: Boolean;
  sepLen: Longint;
Begin
  result := true;
  bInLoop := false;
  Try
    //Liste leeren
    Liste.clear;
    strTemp := Text2Split;
    sepLen := Length(SeperatorStr);
    Posi := Pos(SeperatorStr, strTemp);
    While Posi > 0 Do Begin
      bInLoop := true;
      strPart := Copy(strTemp, 1, Posi - 1);
      Liste.Add(strPart);
      strTemp := copy(strTemp, Posi + sepLen, Length(strTemp) - (Posi + sepLen - 1));
      Posi := Pos(SeperatorStr, strTemp);
    End;
    If (bInLoop) Or (Length(strTemp) > 0) Then
      Liste.add(strTemp);
  Except
    Result := false;
  End;
End;

Function TFileReceiver.Start(Path_to_Save_in: String): Boolean;
Var
  bError: Boolean;
  bReady: Boolean;
  fs: TFileStream;
  downloadTime : Cardinal;
Begin
  result := true;
  If iFileSize > 0 Then Begin
    // Alle Startwerte setzen
    bError := false;
    bReady := false;
    iReceivedBytes := 0;
    // erstmal versuchen die Datei zu erstellen
    // das Zielverzeichnis wo die Daten gespeichert werden sollen könnt Ihr nachher selber bestimmen
    If directoryexists(Path_to_Save_in) Then Begin
      sFileName := Path_to_Save_in + sFileName;
    End
    Else Begin
      // Fehler beim Erstellen der Datei aufgetreten
      result := false;
      sFileName := '';
      exit;
    End;
    Try
      fs := TFileStream.Create(sFileName, fmCreate Or fmShareExclusive);
    Except
      // Fehler beim Erstellen der Datei aufgetreten
      result := false;
      sFileName := '';
      exit;
    End;
    Try
      // Solange keine Abbruch Bediengung erreicht ist Stream-Pakete lesen
      While (Not AThread.Terminated) And (AThread.Connection.Connected) And
        (Not bError) And (Not bReady) Do Begin
        // Buffer(Speicher-Stream) leeren
        tmpMS.clear;
        Try
          // versuchen Stream zu Lesen
          downloadTime := GetTickCount;
          AThread.Connection.ReadStream(tmpMS);
          downloadTime := GetTickCount - downloadTime;
          // Steht jetzt auch wirklich was im Stream drin
          If tmpMS.Size > 0 Then Begin
            // die gelesenen Bytes jetzt direkt in den FileStream schreiben

            if downloadTime > 0 then
              iDownloadRate := Round(tmpMS.Size * 8 / 1024 / downloadTime)
            else
              iDownloadRate := 0;

            fs.copyFrom(tmpMS, 0);
            // Anzahl der gelesenen Bytes erhöhen
            iReceivedBytes := iReceivedBytes + tmpMS.Size;
            // jetzt durch den Thread die Methode UpdateProgress ausführen
            // dieses muss mit Syncronize gemacht werden, mehr dazu in Delphi Hilfe
            AThread.Synchronize(UpdateProgress);
          End;
          bReady := (fs.Size = iFileSize);
        Except
          // Fehler beim Lesen des Stream aufgetreten, Speicher leeren
          tmpMS.Clear;
          // Vorgang abbrechen
          bError := true;
        End;
      End;
    Finally
      fs.free;
      If bError Then Begin
        DeleteFile(PChar(sFileName));
        sFileName := '';
      End;
    End;
    result := FileExists(sFileName);
  End;
End;

procedure TFileReceiver.UpdateProgress;
var
  percent : Integer;
begin
  percent := Round(iReceivedBytes / iFileSize * 100);

  // Label anpassen
  fLabRe.Caption := Format('Received Bytes: %f KB (%d %%) @ %d kbit/s', [iReceivedBytes / 1024, percent, iDownloadRate]);
  // neue Position setzen
  fpBar.Position := percent;
  // GroupBox und alle Unterelemente neu zeichnen
  fgb.Repaint;
end;

End.
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
Benutzerbild von Gausi
Gausi

Registriert seit: 17. Jul 2005
880 Beiträge
 
Delphi 11 Alexandria
 
#4

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 13:59
Zitat von xZise:
OOPs... Ich hatte mich vertan sry Und zwar wird das doch nicht im Thread erstellt...
Hab ich mir fast gedacht, dnn dann dürfte es imho sofort ganz fürchterlich knallen

Bei CreateElements wird ja einmal auf irgendwelche Eigenschaften des Threads zugegriffen - geht das in Ordnung? Sind diese Zugriffe sicher?
  Mit Zitat antworten Zitat
Benutzerbild von RavenIV
RavenIV

Registriert seit: 12. Jan 2005
Ort: Waldshut-Tiengen
2.875 Beiträge
 
Delphi 2007 Enterprise
 
#5

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 14:36
Ich mache das immer so, dass der Thread dem Hauptprogramm eine Message schickt.
Das Hauptprogramm macht dann die Arbeit, die der Thread haben will.
Klaus E.
Linux - das längste Text-Adventure aller Zeiten...
Wer nie Linux mit dem vi konfiguriert hat, der hat am Leben vorbei geklickt.
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#6

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 14:45
Zitat von Gausi:
Zitat von xZise:
OOPs... Ich hatte mich vertan sry Und zwar wird das doch nicht im Thread erstellt...
Hab ich mir fast gedacht, dnn dann dürfte es imho sofort ganz fürchterlich knallen

Bei CreateElements wird ja einmal auf irgendwelche Eigenschaften des Threads zugegriffen - geht das in Ordnung? Sind diese Zugriffe sicher?
Was meinst du damit?

Zitat von RavenIV:
Ich mache das immer so, dass der Thread dem Hauptprogramm eine Message schickt.
Das Hauptprogramm macht dann die Arbeit, die der Thread haben will.
Öhm bisshen kompliziert... Und voher gings ja... nur nachdem ich zwei Labels gestrichen habe und die Updateprozedur verändert habe fing das desatser an...
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
Benutzerbild von RavenIV
RavenIV

Registriert seit: 12. Jan 2005
Ort: Waldshut-Tiengen
2.875 Beiträge
 
Delphi 2007 Enterprise
 
#7

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 14:48
Willst Du es lieber einmal kompliziert für den Programmierer oder
immer kompliziert für den Anwender weil es ständig abschmiert?

Aus einem Thread heraus darf man eben nicht an VCL-Komponenten herumschrauben...
Klaus E.
Linux - das längste Text-Adventure aller Zeiten...
Wer nie Linux mit dem vi konfiguriert hat, der hat am Leben vorbei geklickt.
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

Registriert seit: 3. Mär 2006
Ort: Waldbronn
4.303 Beiträge
 
Delphi 2009 Professional
 
#8

Re: GroupBox auf Mainform => Repaint in Thread = Kill

  Alt 13. Jul 2007, 20:16
Zitat von RavenIV:
Willst Du es lieber einmal kompliziert für den Programmierer oder
immer kompliziert für den Anwender weil es ständig abschmiert?

Aus einem Thread heraus darf man eben nicht an VCL-Komponenten herumschrauben...
Zitat von xZise:
[...]
Öhm bisshen kompliziert... Und voher gings ja... nur nachdem ich zwei Labels gestrichen habe und die Updateprozedur verändert habe fing das desatser an...
Und außerdem schmiert er nur hier ab. In anderen Programmen funktioniert das einwandfrei.

Abgesehen davon, dass man damit nicht das Problem löst, sondern nur dem aus den Weg geht!

[edit]An sich schraube ich doch nicht aus einen Thread darin herum oder? Die Prozedur gehört doch zum Object?
Aber ich würde dennoch mal gerne sehen, wie du das machst ...[/edit]
Fabian
Eigentlich hat MS Windows ab Vista den Hang zur Selbstzerstörung abgewöhnt – mkinzler
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:46 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