Delphi-PRAXiS
Seite 1 von 2  1 2   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Cross-Platform-Entwicklung (https://www.delphipraxis.net/91-cross-platform-entwicklung/)
-   -   Label aktualisieren / TTask (https://www.delphipraxis.net/206614-label-aktualisieren-ttask.html)

greenmile 12. Jan 2021 12:08

Label aktualisieren / TTask
 
Hallo,

ich bin ja aktuell dabei, mein Android Projekt von XE8 auf XE 10.3.3 umzustellen. Während in der 8 noch intensiv mit Application.Processmessages gearbeitet wurde (ja, war und ist böse, aber der Zweck heiligt die Mittel) funktioniert das ja in 10.3.3 nicht so wie bisher. Ich habe zB TAniIndicator. Wenn nun in der App etwas Rechenintensives passiert oder die App einfach mal auf was warten muss, dann wurde der TAniIndicator bisher mit Processmessages erfolgreich aktualisiert. Das klappt nun nicht mehr und sieht blöd aus, so als wenn alles hängt. Also versuche ich es so:

Code:
procedure ...UpdateComponents;
begin
  ...
  If (progPlsWait) then progPlsWait.Repaint;
  ...
end;
Habe dann versucht, über einen HG-Thread die Proc dauernd zu aktualisieren:

Code:
   CompUpdateTask := TTask.Create(
     procedure
     begin
       Sleep(50);
       TThread.Synchronize(TThread.Current,
         procedure
         begin
           UpdateComponents;
         end);
     end);
   CompUpdateTask.Start;
... und, wenn die Berechnung durch ist, das ganze freizugeben

Code:
  CompUpdateTask.Cancel;
  FreeAndNil(CompUpdateTask);
So, der Profi lacht, aber das funktioniert nicht, TAniIndicator wird nicht aktualisiert. Was mache ich falsch? Durchläuft CompUpdateTask nur einmal und ist danach tot, wie ein Thread auch?

TiGü 12. Jan 2021 13:08

AW: Label aktualisieren / TTask
 
Zitat:

Durchläuft CompUpdateTask nur einmal und ist danach tot, wie ein Thread auch
Dir fehlt ja auch die Schleife.
Stelle dir einfach vor, die anonyme Funktion wäre das Execute einer klassischen TThread-Instanz.
Dann startet der Thread, wartet 50 Millisekunden und ruft einmal (!) TThread.Synchronize auf.

greenmile 12. Jan 2021 13:56

AW: Label aktualisieren / TTask
 
Ah, ok, quasi "while not terminated"? Wenn ich es recht verstehe, muss ich "CheckCanceled" aufrufen, das wirft eine Exception, wenn abgebrochen werden soll.
Passt das so? Die While Bedingung ist echt dumm.

Code:
   CompUpdateTask := TTask.Create(
     procedure
     begin
       While (1=1) do begin
         CompUpdateTask.CheckCanceled;

         Sleep(500);
         TThread.Synchronize(TThread.Current,
           procedure
           begin
             UpdateComponents;
           end);
        End;
     end);
   CompUpdateTask.Start;

himitsu 12. Jan 2021 14:48

AW: Label aktualisieren / TTask
 
Und "eventuell" aufpassen, dass
Delphi-Quellcode:
CompUpdateTask.Cancel;
FreeAndNil(CompUpdateTask);
nicht im Hauptthread ausgeführt werden (vor allem niemals innerhalb von UpdateComponents),
denn wenn Cancel oder Free warten, während das Synchronize hängt, dann hast einen Deadlock.

Tipp: Auch in das Synchronize am Anfang ein CheckCanceled rein.

greenmile 12. Jan 2021 14:55

AW: Label aktualisieren / TTask
 
Zitat:

Zitat von himitsu (Beitrag 1480845)
Und "eventuell" aufpassen, dass
Delphi-Quellcode:
CompUpdateTask.Cancel;
FreeAndNil(CompUpdateTask);
nicht im Hauptthread ausgeführt werden (vor allem niemals innerhalb von UpdateComponents),
denn wenn Cancel oder Free warten, während das Synchronize hängt, dann hast einen Deadlock.

Tipp: Auch in das Synchronize am Anfang ein CheckCanceled rein.

Danke für den Tipp, aber ich muss doch vom Haupthread aus Cancel setzen, damit der TTask abgebrochen und beendet wird.
FreeAndNil war übrigens auch blöd, ein := NIL besser.

Nachtrag: Funktioniert übrigens nicht, TAniIndicator wird, genau wie Labels, nicht aktualisiert; repaint reicht wohl nicht. Die 'Arbeit' kann ich auch nicht auslagern, ich muss zB einen Browser aufrufen und der läuft im Hauptthread. Während ich auf den Warte, tut sich unter 10.3.3 dann mal nix, auch kein drehendes 'Bitte warten' Icon.

Irgendwie war das mit Processmessages einfacher. Zumindest hat sich was getan für den User.

greenmile 13. Jan 2021 11:05

AW: Label aktualisieren / TTask
 
Ich verzweifel noch. Ich benötige ein funktionierendes Delay, also um etwas bei Bedarf künstlich zu verzögern. Nehmen wir als Beispiel eine Testversion: Wenn es sich bei der App um eine Testversion handelt, dann soll 10 Sekunden gewartet werden (Delay 10), ansonsten direkt weitergemacht werden. Ich bekomme es einfach nicht hin. Es wird zwar gewartet, in der Wartezeit werden aber keine Nachrichten verarbeitet, ich kann also kein Warte-Progress anzeigen.

Unter Windows ganz einfach:

Code:
  Ende := GetTickCount+mSek;

  While (Ende>GetTickCount) do begin
    Sleep(5);
    Application.ProcessMessages;
  end;
Im Android funktioniert zwar die Wartezeit (klar, was soll auch passieren), es wird aber nichts verarbeitet, da Processmessages nicht funktioniert. Habe es auch schon mit einem Thread versucht in der Hoffnung, dass während 'WaitFor' Nachrichten wie Repaint verarbeitet werden ... Geht aber auch nicht. Obwohl ich Label.Repaint aufrufe, sehe ich die Änderungen auf dem Handy nicht. Beispiel:

Code:
Label1.Text := 'Hallo';
Delay(1000);
Label1.Text := 'Welt';
Delay(1000);
Label1.Text := 'Bin';
Delay(1000);
Label1.Text := 'Fertig';
Ist ein nonsens Code. Im Ergebnis ist entweder nichts sichtbar. Oder nur 'Fertig', selbst wenn ich im Delay "Label1.Repaint" aufrufe. Mache ich das unter Windows, dann steht dort im Sekundentakt 'Hallo' / 'Welt' / 'Bin' / 'Fertig'. Selbst mit einem Timer kann ich es nicht lösen, weil ich ja auch auf seine Beendigung warten muss.

Bin ich wirklich der einzige, der sowas braucht?

TigerLilly 13. Jan 2021 13:36

AW: Label aktualisieren / TTask
 
Ich hab so ein ähnliches Problem mit einem Timer gelöst. Der Timer feuert jede Sekunde, dann kanst du eine Bedingung testen und dann zB eine Bedingung umschalten.

himitsu 13. Jan 2021 16:06

AW: Label aktualisieren / TTask
 
Wenn das System grundsätzlich nicht mit "derartigen" Messages arbeitet, dann bringt es auch nicht viel die Messages verarbeiten zu wollen, welche es nicht gibt.

Hier mal der Pfad für Android: Thread.Synchronize und Application.OnIdle :zwinker:
Delphi-Quellcode:
procedure TApplication.ProcessMessages; // das aus FMX.Forms.pas
var
  AppService: IFMXApplicationService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationService, AppService) then
    while AppService.HandleMessage do { loop };
end;

function TPlatformAndroid.HandleMessage: Boolean;
begin
  InternalProcessMessages;
  Result := False;
end;

procedure TPlatformAndroid.InternalProcessMessages;
begin
  CheckSynchronize;
  ProcessOnIdleEvent;
end;
Ich weiß nicht wie Android seine "Events" verwaltet und ob es in Delphi, bzw. im ADK eine API gibt, womit man anstehende Events jetzt verarbeiten kann ... wenn ja, dann sollte Emba Dieses aber besser mal ins ProcessMessages/HandleMessage einfügen.

greenmile 13. Jan 2021 16:11

AW: Label aktualisieren / TTask
 
Zitat:

Zitat von himitsu (Beitrag 1480932)
Wenn das System grundsätzlich nicht mit "derartigen" Messages arbeitet, dann bringt es auch nicht viel die Messages verarbeiten zu wollen, welche es nicht gibt.

Hier mal der Pfad für Android: Thread.Synchronize und Application.OnIdle :zwinker:
Delphi-Quellcode:
procedure TApplication.ProcessMessages; // das aus FMX.Forms.pas
var
  AppService: IFMXApplicationService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationService, AppService) then
    while AppService.HandleMessage do { loop };
end;

function TPlatformAndroid.HandleMessage: Boolean;
begin
  InternalProcessMessages;
  Result := False;
end;

procedure TPlatformAndroid.InternalProcessMessages;
begin
  CheckSynchronize;
  ProcessOnIdleEvent;
end;

Heißt auf Deutsch, bringt nix? Wann werden denn die zB Labels gezeichnet? Wenn ich es mit Repaint aufrufe, wird nix neu gezeichnet. Und wenn es keine Nachrichten gibt die verarbeitet werden, wie aktualisiere ich dann von Hand, ohne drauf zu warten, dass es der MainThread irgendwie erledigt?

TigerLilly 13. Jan 2021 21:34

AW: Label aktualisieren / TTask
 
@greenmile: Ich glaube, du musst umdenken. Sowas wie "warten" funktioniert nicht. Du kannst "nachschauen", ob dein Thread schon fertig ist. Dein "Nag-Screen" wäre vielleicht so zu implementieren:


- MainForm anzeigen
- Thread starten + am Mainform ein Panel mit Infos anzeigen
- gleichzeitig einen Timer starten, der jede Sekunde nachschaut, ob der Thread schon fertig ist
- wenn ja, das Panel verstecken und den mainform "freigeben" und den Timer beenden

Hat für mich das Problem "Form schließen wenn Task noch läuft" gut gelöst. Aber ich bin offen für Fortbildung. :-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:12 Uhr.
Seite 1 von 2  1 2   

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