Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Form ansprechen während einer Bearbeitung (https://www.delphipraxis.net/162192-form-ansprechen-waehrend-einer-bearbeitung.html)

Jens01 11. Aug 2011 13:53

Form ansprechen während einer Bearbeitung
 
Ich möchte ein Fenster haben, wie Delphi beim Compilieren.

Ich habe eine Berechnung, die ca. 2 Minuten dauert, dabei soll ein Fenster den Status mit einem Progressbar anzeigen, ein Button soll zum Abbrechen sein und das Fenster soll verschiebbar sein.
Das Fenster, welches ich vor der Berechnung aufrufe, friert aber ein. Erst nach der Berechnung ist es wieder ansprechbar.

Wie kann ich soetwas gestalten?

-187- 11. Aug 2011 13:56

AW: Form ansprechen während einer Bearbeitung
 
Du musst die Berechnungsfunktion in ein extra Thread auslagern.

Alternativ, als schlechten Turn-Around, könntest du Application.ProcessMessages benutzen (In einer Schleife zb). Zeig doch mal deine Berechnungsfunktion.

Jens01 11. Aug 2011 14:22

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Zeig doch mal deine Berechnungsfunktion.
Würde ich gerne zeigen, wenn es nicht etwas umständlich wäre.
Aber eigentlich ist es eine for-Schleife mit 4-5 Verzweigungen in Unterberechnungen die jeweils ca 100ms dauern.

Gesucht habe ich wohl dies "Application.ProcessMessages", was ich schon mal aufgegriffen hatte, aber im Lauf der Zeit vergessen habe.
Das mit den Thread hatte ich mir schon überlegt, aber ich habe bis jetzt damit noch nicht gearbeitet. Vielleich guck ich mir das jetzt mal an.

Problem ist aber auch, dass während der Berechnungen nur dieser Abbuch-Button betätigt werden darf. Andere Eingaben dürfen nicht getätigt werden, da das sonst in die Berechnung eingehen könnte.

himitsu 11. Aug 2011 14:36

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Zitat von Jens01 (Beitrag 1116265)
Andere Eingaben dürfen nicht getätigt werden, da das sonst in die Berechnung eingehen könnte.

Alles außer dem Button disablen.

Jens01 11. Aug 2011 14:42

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Alles außer dem Button disablen.
Den Gedankengang hatte ich eben auch. Dann muß aber jedes Controll einzeln gedisabled werden. Ganz schön viel.
Hatte eben gerade versucht ganz simpel das Fenster zu disablen, aber dann friert es ein.

DeddyH 11. Aug 2011 14:47

AW: Form ansprechen während einer Bearbeitung
 
Pack die Controls außer dem Abbrechen-Button auf ein Panel und disable dann das.

BUG 11. Aug 2011 14:54

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Zitat von Jens01 (Beitrag 1116265)
Andere Eingaben dürfen nicht getätigt werden, da das sonst in die Berechnung eingehen könnte.

Soll das heißen du liest innerhalb der Schleifen Daten aus der GUI (mit String-Konverierung)?
Es würde mich wundern, wenn deine Bearbeitung nicht schneller werden würde, wenn du das stärker trennst.

DeddyH 11. Aug 2011 14:56

AW: Form ansprechen während einer Bearbeitung
 
Das hoffe ich doch nicht, aber irgendwelche Schaltflächen könnten schon dazu führen, dass man plötzlich mit falschen Werten rechnet.

Jens01 11. Aug 2011 15:06

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Soll das heißen du liest innerhalb der Schleifen Daten aus der GUI (mit String-Konverierung)?
Nein, aber bei (z.B.) einem Edit wird nach der Eingabe das Datum sofort in das Feld/Variable geschrieben, die dann während der Berechnung angesprochen werden könnte.

himitsu 11. Aug 2011 15:17

AW: Form ansprechen während einer Bearbeitung
 
Dann übergib die Werte der GUI nur in den Buttons, welche die aktion starten.
Ist dieser Button dann gesperrt, dann hat eine Änderung des GUI--Inhalts keine Auswirkung, bzw. nutze für die Berechnung jeweils eine Kopie der Werte.

sirius 11. Aug 2011 15:27

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Zitat von Jens01 (Beitrag 1116272)
Zitat:

Alles außer dem Button disablen.
Den Gedankengang hatte ich eben auch. Dann muß aber jedes Controll einzeln gedisabled werden. Ganz schön viel.
Hatte eben gerade versucht ganz simpel das Fenster zu disablen, aber dann friert es ein.


Du hast ein schönes großes Fenster, wo man also ne Menge eingeben kann. Dann klickt man auf einen Button "Start" und es soll ein neues Fenster mit einer Processbar und einem Abbruchbutton darauf erscheinen. Das schöne große Fenster soll disablen (sowie der Rest des Programms auch, falls noch andere schöne Fenster da sind) und nur die das zuletzt geöffnete Fenster mit der Progressbar soll eine Aufgabe übernehmen, die etwas Zeit beansprucht. Die Progressbar soll den Fortschritt anzeigen und der Abbruchbutton soll jederzeit die Unterbrechung der Aufgabe ermöglichen.
Am Ende der Aufgabe (oder nach Unterbrechung) soll sich das FEnster wieder schließen und die anderen schönen großen Fenster sollen wieder enablen.

Ist das soweit richtig?

Dann starte das ProgressbarFenster einfach modal (ShowModal), damit ist alles andere grau. Und das Progressbarfenster startet den Thread mit der Aufgabe.

Es gäbe auch noch die Funktion DisableTaskWindows, aber eigentlich übernimmt showmodal schon alles.

Jens01 11. Aug 2011 15:38

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Ist das soweit richtig?
Ja!
Zitat:

Dann starte das ProgressbarFenster einfach modal (ShowModal), damit ist alles andere grau. Und das Progressbarfenster startet den Thread mit der Aufgabe.
Das könnte gehen. Ich probier das mal so... danke!

sirius 11. Aug 2011 16:31

AW: Form ansprechen während einer Bearbeitung
 
Hier wäre mal ein Vorschlag

Form1 ist via Formdesigner einer normalen VCL-Anwendung erstellt
ProgressForm ist direkt gecodet (aber nur als Beispiel; kann man auch über den Designer erstellen und zu den "verfügbaren Formularen" in den Projektoptionen schieben).

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;


const
  CM_Progress=WM_User+1;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;


  TProgressThread=class(TThread)
  protected
    procedure execute; override;
  private
    FParameter:String;
    FProgressWindowHandle:hwnd;
    FResult:String;
    FStatusReady: boolean;
    procedure SetParameter(const Value: String);
    procedure SetProgressWindowHandle(const Value: hwnd);
  public
    property ProgressWindowHandle:hwnd read FProgressWindowHandle write SetProgressWindowHandle;
    property Parameter:String read FParameter write SetParameter;
    property StatusReady:boolean read FStatusReady;
    property Result:String read FResult;
  end;

  TProgressform=class(TCustomForm)
    constructor CreateNew(AOwner: TComponent;dummy:Integer); override;
  private
    FProgressbar:TProgressbar;
    FAbbruchButton:TButton;
    FProgressThread:TProgressThread;
    FLabel:TLabel;
    FResult: String;
    FParameter: String;
    procedure AbbruchButtonClick(Sender:TObject);
    procedure CMProgress(var msg:TMessage);message CM_Progress;
    procedure SetParameter(const Value: String);
    procedure StarteThread(Parameter: String);
    procedure ProgressThreadTerminate(Sender:TObject);
  protected
    procedure DoShow; override;
  public
    property Parameter:String read FParameter write SetParameter;
    property Result:String read FResult;
  end;



var
  Form1: TForm1;

implementation

{$R *.dfm}




procedure TForm1.Button1Click(Sender: TObject);
var Progressform:TProgressform;
begin
  //Form dynamisch erstellen
  Progressform:=TProgressform.CreateNew(nil,0);
  try
    //Eingansgparameter setzen
    ProgressForm.Parameter:=Edit1.Text;

    //Fenster Modal öffnen und Rückgabewert prüfen
    case Progressform.ShowModal of
      mrOK: Memo1.Lines.Add('Fertsch: '+ProgressForm.Result);
      mrCancel: Memo1.Lines.Add('Abbruch: '+ ProgressForm.Result);
    end;
  finally
    //finito
    Progressform.Free;
  end;
end;



{ TProgressform }

procedure TProgressform.AbbruchButtonClick(Sender: TObject);
begin
  //einfach nur Abbruch des Threads erzwingen
  //um auch einen konsistenten Zustand aller Werte des Threads zu haben
  FProgressThread.Terminate;
end;

procedure TProgressform.CMProgress(var msg: TMessage);
begin
  //zum Aktualisieren der
  //Wenn Postmessage an dieses Fenster gesendet wird... (siehe TProgressthread.execute)
  FProgressbar.StepBy(msg.LParam); //lparam ist der letzte Wert in Postmessage
  FLabel.Caption:=
    inttostr(FProgressbar.Position)+' von '+inttostr(FProgressbar.Max);

  //lparam und wParam dürfen frei verwendet werden
end;

constructor TProgressform.CreateNew(AOwner: TComponent;dummy:Integer);
begin
  //hier wird ein fesnter ohne formulardesigner erstellt
  //kann man aber auch mit designer machen (der Code aus Methode DoShow muss dan zum Ereignis onShow)
  inherited;

  //lustige Elemente zusammenbasteln
  Caption:='please hold the line...';
  Width:=400;
  Height:=150;
  FProgressbar:=TProgressbar.Create(self);
  FProgressbar.Parent:=self;
  FProgressbar.Height:=20;
  FProgressbar.Width:=300;
  FProgressbar.Left:=50;
  FProgressbar.Top:=20;
  FProgressbar.Visible:=true;
  FPRogressbar.Min:=0;
  FPRogressbar.Max:=100;
  FProgressbar.Position:=0;

  FAbbruchButton:=TButton.Create(self);
  FAbbruchButton.Caption:='A&bbruch';
  FAbbruchbutton.Parent:=self;
  FAbbruchbutton.Height:=40;
  FAbbruchButton.Width:=100;
  FAbbruchButton.Left:=150;
  FAbbruchButton.Top:=60;
  FAbbruchButton.Show;
  FAbbruchButton.OnClick:=AbbruchButtonClick;

  FLabel:=TLabel.Create(self);
  FLabel.Parent:=self;
  FLabel.Visible:=true;
  FLabel.Caption:='ich muss mal kurz rechnen';
  FLabel.Left:=50;
  FLabel.Top:=5;
end;

procedure TProgressform.DoShow; //oder als Ereignis in onShow
begin
  inherited;
  BringToFront;
  if FParameter<>'' then
    StarteThread(FParameter) //wenn Fenster angezeigt wird, soll der Thread gestartet werden
  else
    raise Exception.Create('keine Parameter'); //aber nur bei gültigen Eingabewerten ;-)
end;

procedure TProgressform.ProgressThreadTerminate(Sender: TObject);
begin
  //wenn der Thread beendet wird, landen wir hier
  FResult:=(Sender as TProgressThread).Result;

  if (Sender as TProgressThread).StatusReady then
    modalResult:=mrOk
  else
    modalResult:=mrCancel;
  //das setzen von modalresult bewirkt, dass dieses Fenster automatisch geschlossen wird
  // wir landen hinter dem Aufruf von ShowModal
end;

procedure TProgressform.SetParameter(const Value: String);
begin
  FParameter := Value;
  FProgressbar.Max:= length(FParameter);
end;

procedure TProgressform.StarteThread(Parameter: String);
begin
  FProgressThread:=TProgressThread.Create(true);
  //Parameter übergeben
  FProgressThread.Parameter:=FParameter;
  //Windowhandle übergeben, damit wir Messages des Threads über den Fortschritt bekommen können
  FProgressThread.ProgressWindowHandle:=self.WindowHandle;
  FProgressThread.OnTerminate:=ProgressThreadTerminate;
  FProgressThread.FreeOnTerminate:=true;
  FProgressThread.Resume;
end;

{ TProgressThread }

procedure TProgressThread.execute;
var i,p:Integer;
begin
  FStatusready:=false;
  p:=1;

  //Hauptschleife
  while (not terminated)and(p<=length(FParameter)) do
  begin
    //erstmal Message senden, dass wir die nächste Aufgabe abarbeiten
    Postmessage(FProgressWindowHandle,CM_Progress,0,1);

    //Aufgabe erledigen
    FResult:=FResult+inttohex(ord(FParameter[p]),2)+' ';
    inc(p);

    //simulieren, dass die Aufgabe ca. 1sek braucht
    for i:=1 to 10 do
    begin
      sleep(100);
      if terminated then break;
    end;

  end;


  FStatusReady:= p>length(FParameter);
end;

procedure TProgressThread.SetParameter(const Value: String);
begin
  FParameter := Value;
end;

procedure TProgressThread.SetProgressWindowHandle(const Value: hwnd);
begin
  FProgressWindowHandle := Value;
end;

end.

Jens01 11. Aug 2011 16:47

AW: Form ansprechen während einer Bearbeitung
 
Danke, das muuß ich mir erst einmal angucken. Irgendwie alles komplizierter als ich dachte...

Luckie 11. Aug 2011 16:48

AW: Form ansprechen während einer Bearbeitung
 
Nein, er hat es nur komplizierter gemacht als nötig, weil er, warum auch immer, das Formular zur Laufzeit erstellt, was absolut unnötig ist.

sirius 11. Aug 2011 17:00

AW: Form ansprechen während einer Bearbeitung
 
Zitat:

Zitat von Luckie (Beitrag 1116304)
Nein, er hat es nur komplizierter gemacht als nötig, weil er, warum auch immer, das Formular zur Laufzeit erstellt, was absolut unnötig ist.

Unnötig oder gleichwertig? So habe ich das Beispiel schön in eine Unit gepresst.

Der einzige Unterschied ist die Methode CreateNew, die man sich sonst zusammenklickern anstatt tippen müsste.

Jens01 11. Aug 2011 17:09

AW: Form ansprechen während einer Bearbeitung
 
Ganz ruhig.
Mit "kompliziert" habe ich mich auf das ganze Thema bezogen. Eigentlich dachte ich, ich mache ein kleines Fenster auf mit einem Progressbar und einem Button und das wäre es schon fast, aber...

DeddyH 11. Aug 2011 17:12

AW: Form ansprechen während einer Bearbeitung
 
Auf das Problem ist wohl jeder schon gestoßen.

Jens01 11. Aug 2011 18:05

AW: Form ansprechen während einer Bearbeitung
 
In den Cindy-Komponenten habe ich ein ProgressPanel gefunden und das wie Folgt adaptiert.
Die Threadlösung ist derzeit etwas zu groß für mich.
Bei dieser Lösung frieren aber auch alle Fenster ein. Das Progressfenster zeigt aber die Bars an und der Button kann den Prozess canceln.
Delphi-Quellcode:
type
  TfrmProgress = class(TForm)
    pb2: TProgressBar;
    pb1: TProgressBar;
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
  public
    IsCanceled: Boolean;
    function ProcessMessages(var Msg: TMsg): Boolean; overload;
    procedure ProcessMessages; overload;
  end;

var
  frmProgress: TfrmProgress;

implementation

{$R *.dfm}

procedure TfrmProgress.ProcessMessages;
var
  Msg: TMsg;
  Cont: Boolean;
begin
  cont := ProcessMessages(Msg);

  while Cont do
    cont := ProcessMessages(Msg);
end;

procedure TfrmProgress.btn1Click(Sender: TObject);
begin
  IsCanceled := True;
end;

procedure TfrmProgress.FormShow(Sender: TObject);
begin
  pb1.Position := 0;
  IsCanceled := False;
end;

function TfrmProgress.ProcessMessages(var Msg: TMsg): Boolean;
var
  aHandle: HWND;
begin
  RESULT := false;
  if IsCanceled then
    EXIT;
  aHandle := 0;   // aHandle := FPanel.Handle doesn' t work if you click outiside the panel before clicking panel's cancel button ...

  if PeekMessage(Msg, aHandle, 0, 0, PM_REMOVE) then
  begin
    Result := True;

    if Msg.hwnd = btn1.Handle then
    begin
      Windows.TranslateMessage(Msg);
      Windows.DispatchMessage(Msg);
    end
    else if Msg.message = WM_PAINT then // !!! Only WM_PAINT message are not removed from queue         !!!
    begin                            // In order to go outside while Cont do, we need to dispatch the message //
      Windows.TranslateMessage(Msg);
      Windows.DispatchMessage(Msg);
    end;
  end;
end;
Die Berechnungsschleife:
Delphi-Quellcode:
frmProgress.Show;
for i:= 0 to 1000
begin
  Berechnung;
  frmProgress.ProcessMessages;
  if frmProgress.IsCanceled then
    break;
end;
frmProgress.Close;

sirius 12. Aug 2011 09:06

AW: Form ansprechen während einer Bearbeitung
 
Wenn es funktioniert ist es ok. Aber bedenke, dass du hier auch ein separate Messageschleife benutzt. Es ist zwar nicht die von TApplication(.ProcessMessages), sondern deine eigene (damit du Messages filtern kannst). Aber bei größeren Programmen bekommst du mit derartigen Konstrukten nur Probleme.
Lies Dir mal folgenden Beitrag von einem sehr geschätzten Informatiker durch (es geht um Delay, welches auch Application.ProcessMessages benutzt):
http://www.delphipraxis.net/739535-post17.html (zweiter Absatz)


Ich will Dir damit sagen: Früher oder später kommst du um Threads nicht herum. Kein ordentliches Programm kommt ohne Threads aus. Das ist nicht so kompliziert. Du solltest es dir an dem recht einfachen Beispiel einer Progressbar einmal ansehen.

mfg
sirius

Jens01 12. Aug 2011 12:03

AW: Form ansprechen während einer Bearbeitung
 
@sirius
Gestern Nacht habe ich das noch zum Laufen bekommen. Und es funktioniert schon ganz gut. Ich glaube nicht, dass es sich so auswirkt wie negaH es beschieben hat. Da es nur eine Progressform ist, die nach der Berechnung beendet wird.
Grundsätzlich arbeite ich event-orientiert (außer an ein, zwei kleinen Stellen, aber ich bin ja kein Informatiker sondern komme vom Bau:mrgreen:)

Du hast mich aber davon überzeugt mich damit zu beschäftigen!
Gruss Jens


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:28 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