Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Anwendung bleibt komplett stehen (https://www.delphipraxis.net/99315-anwendung-bleibt-komplett-stehen.html)

Phoenix 10. Sep 2007 15:43


Anwendung bleibt komplett stehen
 
Ich baue gerade eine Visualisierung für eine SPS.
Dazu müssen einige Controls synchron im Sekundentakt blinken. Hierfür benutze ich einen Timer, der, solange das Formular das aktive Form der Anwendung ist, alle Sekunde das Zeichnen auslöst.

Dazu benutze ich folgendes Konstrukt:
Delphi-Quellcode:
// aus der Redraw-Routine
      Application.ProcessMessages;

      //*** change Flash State every time we refresh ourself:
      FControlFlashState := not FControlFlashState;

      //*** stop redrawing
      SendMessage(pnBaseForm.Handle, WM_SETREDRAW, Integer(FALSE), 0);

      //*** update controls:
      for i := 0 to Self.ComponentCount - 1 do
      begin
        if Self.Components[i] is TCustomVisuControl then
        begin
          (Self.Components[i] as TCustomVisuControl).ChangeState(FControlFlashState);
        end;
      end;

      //*** enable und force redrawing
      SendMessage(pnBDMBaseForm.Handle, WM_SETREDRAW, Integer(TRUE), 0);
      InvalidateRect(pnBDMBaseForm.Handle, nil, false);

      //*** force update of controls:
      for i := 0 to Self.ComponentCount - 1 do
      begin
        if Self.Components[i] is TControl then
        begin
          (Self.Components[i] as TControl).Repaint;
        end;
      end;
Im ChangeState des Controls schaut das Control erstmal auf einer Memtable nach, ob für es neue Daten vorliegen. Dann holt es für diese Daten die Farben und ggf, eine Caption ab, und zwar für den Flashstate (wenn ein Control z.B. rot/weiss blinken soll bei Flashstate true -> rot und bei Flashstate false -> weiss).

Da sich das Control beim setzen von Color, Font.Color und/oder Caption ja sonst sofort neu zeichnen würde und das Abholen der Daten auch ein bisschen Zeit braucht, würden die Controls nicht synchron blinken. Deshalb der Behelf mit WM_SETREDRAW.

Das pnBaseForm ist ein Panel, welches auf einem WinControl sitzt welches auf einem Form sitzt welches aber wiederum (ähnlich wie ein Frame) mit noch einigen Controls dazwischen auf dem Mainform sitzt. Das ist durch die Anwendung so vorgegeben, daran kann ich auch nichts ändern.

Das Problem:
Wenn sich das Formular gerade in dieser Routine befindet, und man in diesem Moment aus der Anwendung hinausklickt, dann bleibt die Anwendung stehen. Komplett. Zumindest glaube ich, dass sich die Anwendung genau in dieser Routine befindet, wenn es passiert.

Die CPU-Last geht auf Null Prozent zurück und die Anwendung reagiert gar nicht mehr. Im Debugger kann ich die Ausführung auch nicht mehr anhalten um zu gucken wo er hängt. Die Anwendung muss ich mit dem Taskmanager abschiessen.

Setze ich den Timer auf 5 Sekunden ist das Verhalten schwieriger Nachzuvollziehen - weil der Timer eben nicht so oft tickt und man den richtigen Augenblick abpassen muss.

Was kann ich gegen diesen Totalhänger tun?

Muetze1 10. Sep 2007 15:49

Re: Anwendung bleibt komplett stehen
 
Schonmal ausprobiert es bei einem InvalidateRect() zu belassen und danach nicht ein repaint aufzurufen? Repaint ist eine Kapslung von Invalidate + UpdateWindow() und somit sogar ein doppelter Code.

Auch verhindern das WM_SETDRAW das Zeichnen, aber alle zu zeichnenden Bereiche werden gemerkt und akkumuliert. Von daher sollte das zurücknehmen von WM_SETDRAW ein neuzeichnen auslöen ohne dass du vorher nochmal die einzelnen Elemente invalidieren musst.

Falls nicht, dann würde folgendes reichen:
Delphi-Quellcode:
      //*** enable und force redrawing
      SendMessage(pnBDMBaseForm.Handle, WM_SETREDRAW, Integer(TRUE), 0);
      InvalidateRect(pnBDMBaseForm.Handle, nil, false);

      //*** force update of controls:
      UpdateWindow(pnBDMBaseForm.Handle);

Phoenix 10. Sep 2007 16:00

Re: Anwendung bleibt komplett stehen
 
Zitat:

Zitat von Muetze1
Auch verhindern das WM_SETDRAW das Zeichnen, aber alle zu zeichnenden Bereiche werden gemerkt und akkumuliert. Von daher sollte das zurücknehmen von WM_SETDRAW ein neuzeichnen auslöen ohne dass du vorher nochmal die einzelnen Elemente invalidieren musst.

Tut es aber nicht. Wenn ich nicht jedes einzelne Control Invalidate/Update bzw. eben Repaint aufrufe, werden die Controls NUR dann neu gezeichnet, wenn sie danach nochmal irgendwann verdeckt wurden. Das heisst, die controls blinken nicht, sondern behalten ihre alte Farbe, wenn ich kein Inavlidate aufrufe, nachdem ich SETREDRAW TRUE gesendet habe.

Gerade getestet: UpdateWindow reicht auch nicht. Ich muss explizit jedes einzelne Control invalidieren, sonst wechselt sich die Farbe nicht.

Aber das Problem liegt auch nicht daran, sondern an den zwei WM_SETREDRAW Messages. Nehme ich die raus, kann ich aus der Anwendung raus und wieder reinklicken so oft ich will und wann ich will: Kein Problem. Sobald die beiden Messages drin sind bleibt die Anwendung stehen sobald in rausklicke wenn er dazwischen ist.

Jetzt die Frage: WARUM bleibt die Anwendung überhaupt stehen (es sieht ja fast so aus, als würde die MessageLoop komplett blockieren), und viel wichtiger: Was kann ich dagegen tun?

Die beiden Messages einfach weglassen ist keine Option, denn kann man tatsächlich jedem einzelnen Control beim Neuzeichnen zugucken, und das erste Control blinkt alles andere als synchron mit dem letzten.

shmia 10. Sep 2007 16:06

Re: Anwendung bleibt komplett stehen
 
Wie wäre es damit (Ersatz für Application.ProcessMessages):
Delphi-Quellcode:
Procedure ProcessPaintMessages;
Var
   Msg : TMsg;
Begin
   While PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) Do
   Begin
//      TranslateMessage(Msg); // translate only Keyboard-Messages
      DispatchMessage(Msg);
   End;
End;
Damit werden nur Paint-Messages verarbeitet; alle anderen Messages verbleiben in der Queue.

Phoenix 10. Sep 2007 16:13

Re: Anwendung bleibt komplett stehen
 
Den hab ich eigentlich absichtlich da drin, damit die MessageQueue komplett leer ist, bevor ich diese Updateroutine beginne. Ohne das Ding fühlt sich die Anwendung richtig zäh an, und lässt sich auch nicht richtig bzw. nur extrem ruckelig verschieben.

sirius 10. Sep 2007 16:30

Re: Anwendung bleibt komplett stehen
 
Und das Projekt tut sich nicht zufällig mit dem application.processmessages innerhalb des Timers selbst verschlucken?

Phoenix 10. Sep 2007 16:34

Re: Anwendung bleibt komplett stehen
 
Hatte ich schon getestet. Ich habe den beim Beginn der Prozedur deaktiviert, das Ding in einen Try - finally Block geklemmt und den Timer hinterher wieder aktiviert, so dass das Ereignis nicht kommen konnte, während es da drin war. Anmerkung dazu: Timer deaktivieren bedeutet auf diesen Formularen, dass die Timer-Komponente vernichtet wird. Beim Aktivieren des Autorefreshs wird der Timer dann neu Erzeugt. Also ist das wirklich sicher gewesen. Geholfen hats allerdings nix.


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