Application.ProcessMessages und so ...

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Eclipticon
Beiträge: 292
Registriert: Sa 5. Feb 2011, 20:38
OS, Lazarus, FPC: Windows XP VirtualBox (FPC 2.6.4, Laz 1.2.4)
CPU-Target: 32Bit
Wohnort: Wien

Application.ProcessMessages und so ...

Beitrag von Eclipticon »

Hi,

ich moechte eine Schleife, die nach dem Klick auf einen Button gestartet wird, durch einen Klick auf einen anderen Button abrrechen koennten.

Prinzipiell funktioniert dieser Code ja ziemlich gut:

Code: Alles auswählen

procedure TForm1.LoopButtonClick(Sender: TObject);
begin
  Cancelled := false;
  while (not Cancelled) do begin
    Application.ProcessMessages;
    ProgressBar1.Position := Random(100);
  end;
end;
 
 
procedure TForm1.CancelButtonClick(Sender: TObject);
begin
  Cancelled  := true;
end;
In der Realitaet steht aber sehr viel mehr in dieser Schleife, CancenButton reagiert kaum. Kann ich irgendwas tun (ausser als jede 2. Zeile Application.ProcessMessages aufzurufen), um dem abzuhelfen?

Danke!

Maik81ftl
Beiträge: 619
Registriert: Mi 9. Mär 2011, 16:34
OS, Lazarus, FPC: Ubuntu10.04 LTS (L 0.9.31.0 FPC 2.4.4)
CPU-Target: 64Bit
Wohnort: seit 01.06.2011 in Wahlstedt

Re: Application.ProcessMessages und so ...

Beitrag von Maik81ftl »

Wag mal zu behaupten, das dies nach einer endlosschleife ausschaut.

weiß nicht, ob das geht ggf 'nen break von ca 0.5 sec reinzujagen (z.B. via TTimer oder TProzess).

sonst in dieser schleife eine Abfrage des Button, zum abbrechen. so nach dem Motto clicked = true;
Ubuntu 10.04 LTS ist meine Heimat. Lazarus ist meine Sprache :D und der Kreis Segeberg meine LIEBE :D

Eclipticon
Beiträge: 292
Registriert: Sa 5. Feb 2011, 20:38
OS, Lazarus, FPC: Windows XP VirtualBox (FPC 2.6.4, Laz 1.2.4)
CPU-Target: 32Bit
Wohnort: Wien

Re: Application.ProcessMessages und so ...

Beitrag von Eclipticon »

Da die Variable Cancelled offenbar global ist, kann man die Schleife beenden ...

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Application.ProcessMessages und so ...

Beitrag von theo »

Eclipticon hat geschrieben:Kann ich irgendwas tun (ausser als jede 2. Zeile Application.ProcessMessages aufzurufen), um dem abzuhelfen?
A: Den Code schneller machen.
Oder B: den Code in einen separaten Thread auslagern.

Eclipticon
Beiträge: 292
Registriert: Sa 5. Feb 2011, 20:38
OS, Lazarus, FPC: Windows XP VirtualBox (FPC 2.6.4, Laz 1.2.4)
CPU-Target: 32Bit
Wohnort: Wien

Re: Application.ProcessMessages und so ...

Beitrag von Eclipticon »

Lieber Theo,
theo hat geschrieben:A: Den Code schneller machen.
Oder B: den Code in einen separaten Thread auslagern.
mit dieser Antwort tust du mir keinen Gefallen ;-) A kommt nicht in Frage, da es sich um externen Code handelt, auf den ich keine Zugriff hab, B ist bekannt muehsam ...

Zum Verstaendnis: Ich ging davon aus, dass ein Aufruf von Application.ProcessMessages ALLE in der Message Queue vorhandenen Messages abarbeitet, bevor mein Code weiter ausgefuehrt wird. Inwiefern wuerde mir dann schnellerer Code helfen?

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Application.ProcessMessages und so ...

Beitrag von theo »

Eclipticon hat geschrieben:Inwiefern wuerde mir dann schnellerer Code helfen?
Processmessages würde öfter aufgerufen, und damit würde dein CancelButton weniger träge.

Eclipticon
Beiträge: 292
Registriert: Sa 5. Feb 2011, 20:38
OS, Lazarus, FPC: Windows XP VirtualBox (FPC 2.6.4, Laz 1.2.4)
CPU-Target: 32Bit
Wohnort: Wien

Re: Application.ProcessMessages und so ...

Beitrag von Eclipticon »

Hi,

leider plagt mich dieses Problem nach wie vor ... auch ein Application.ProcessMessages in jeder zweiten Zeile hilft nicht wirklch weiter. Mit

Code: Alles auswählen

for i := 0 to 1000 do
   Application.ProcessMessages
in jede zweiten Zeile reagiert die Oberflaeche deutlich besser.

Aber das kann's doch nicht sein ... besonders, wenn ich mir den Kommentar zu Application.ProcessMessages ansehe und der sagt

Code: Alles auswählen

// "Enter the messageloop and process until empty"
Warum sollte es da einen Unterschied machen, ob ich es 1x oder 1001x aufrufe?

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Application.ProcessMessages und so ...

Beitrag von MAC »

Code: Alles auswählen

procedure TForm1.LoopButtonClick(Sender: TObject);
var
 i:integer = 0;
const 
 pause = 100;
 pausezeit = 10;
begin
  Cancelled := false;
  while (not Cancelled) do 
     begin
     inc(i);
     if i > pause then 
         begin
         i := 0;
         Application.ProcessMessages;
         sleep(pausezeit);
         end; 
     ProgressBar1.Position := Random(100);
     end;
end;

Code: Alles auswählen

Signatur := nil;

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Application.ProcessMessages und so ...

Beitrag von mschnell »

... "sleep" ....

Und was soll das ????

Mit sleep im Mainthread hängt der ganze Mainthread, also sowohl die Ausführung des Arbeits-Codes als auch die Oberfläche. Begünstigt werden dadurch nur andere Threads dieses Programms (die zeigen aber nix an) und andere Programme.

Um die Oberfläche zu beschleunigen hilft nur ProcessMessages oder auslagern des Arbeistscodes in einen Thread.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Application.ProcessMessages und so ...

Beitrag von mschnell »

Eclipticon hat geschrieben:Warum sollte es da einen Unterschied machen, ob ich es 1x oder 1001x aufrufe?
Macht es nicht.
Es kommt auf die maximale Zeit zwischen zwei Aufrufen an. Wenn Du in den externen Code kein ProcessMessages einbauen kannst (z.B. weil es eine DLL oder eine C-Funktion ist), hilft es nur, den externen Code in einem Thread aufzurufen. Und das wiederum ist nur erlaubt, wenn der Code im Thread keine Oberflächen-Aktionen macht (wie z.B. Progress-Bar).

Wenn Du Meldungen vom Thread an den MainThread brauchst, die z.B. ein ProgressBar bewegen, machst Du das am besten mit Application.QueueAsyncCall().

Um den Thread abzubrechen, muss der regelmäßig eine Variable, die vom Mainthread gesetzt wird (vorgesehen dafür ist TThread.Terminated) pollen und sich wenn nötig selbst beenden (seine Hauptschleife verlassen).

Der Mainthread muss dann wiederum z.B. mit TThread.WaitFor warten, ob der Thread auch wirklich beendet ist, bevor er z.B. die Applikation schließen darf.

Während TThread.WaitFor hängt natürlich die Oberfläche, da der Mainthread ja wartet (was er eigentlich nie machen sollte). Deshalb kann es bei möglicherweise langer Wartezeit geschickt sein, den Thread nach der Hauptschleife vor der tatsächlichen Beendigung (z.B. mit Application.QueueAsyncCall() oder TThread.Synchronize() ) eine Mitteilung an den Mainthread schicken zu lassen, damit der so lange noch arbeiten kann (z.B. irgendwas anzeigen).

-Michael

Antworten