Recent

Author Topic: Responsive Form with Animated GIF  (Read 1315 times)

Weitentaaal

  • Hero Member
  • *****
  • Posts: 550
Responsive Form with Animated GIF
« on: May 30, 2024, 02:42:45 pm »
Hello,

i have the case where i send Data to a Server and get some Data back. Now i wanted to Show the user somehow that a connection to the Server is build up. i made a Form like this:

Code: Pascal  [Select][+][-]
  1.  
  2. {$R *.lfm}
  3.  
  4. function SetLayeredWindowAttributes(hWnd: longint; Color: longint;
  5.    X: byte; alpha: longint): bool stdcall; external 'USER32';
  6.  
  7. function SetWindowLongA(hWnd: longint; nIndex: longint;
  8.    dwNewLong: longint): longint stdcall; external 'USER32';
  9.  
  10. function GetWindowLongA(hWnd: longint; nIndex: longint): longint stdcall;
  11.    external 'user32';
  12.  
  13. procedure SetTranslucent(ThehWnd: longint; Color: longint; nTrans: integer);
  14. var
  15.    Attrib: longint;
  16. begin
  17.    {SetWindowLong and SetLayeredWindowAttributes = API functions}
  18.    Attrib := GetWindowLongA(ThehWnd, GWL_EXSTYLE);
  19.    SetWindowLongA(ThehWnd, GWL_EXSTYLE, attrib or WS_EX_LAYERED);
  20.    {anything with color value color will completely disappear if flag = 1 or flag = 3  }
  21.    SetLayeredWindowAttributes(ThehWnd, Color, nTrans, 1);
  22. end;
  23.  
  24. { TGifView }
  25.  
  26. procedure TGifView.FormShow(Sender: TObject);
  27. begin
  28.    Image.Picture.LoadFromFile(PathToGIF);
  29.  
  30.    self.Top:= Application.MainForm.Top + Application.MainForm.Height - Self.Height - 10;
  31.    self.Left:= Application.MainForm.Left + Application.MainForm.Width - Self.Width - 10;
  32. end;
  33.  
  34. procedure TGifView.FormCreate(Sender: TObject);
  35. var
  36.    Transparency: longint;
  37. begin
  38.    Self.Color := clRed;
  39.    Transparency := Self.Color;
  40.    SetTranslucent(Self.Handle, Transparency, 0);
  41.  
  42.    PathToGIF:= '';
  43. end;
  44.  
  45.  

the Code works for me and if i show the form then the Animated gif will pop up in the right bottom corner until i Hide/Close it again.

Now the problem i have is that i use this in this Code:

Code: Pascal  [Select][+][-]
  1. function GetResponse(request: WideString): String;
  2. var
  3.    HsGet: THTTPSend;
  4.    Resp: TStringList;
  5. begin
  6.    HsGet:= THTTPSend.Create;
  7.    Resp:= TStringList.Create;
  8.    GifView.PathToGIF:= ExePath+'pics\ConnToServer.gif';
  9.    GifView.Show;
  10.    try
  11.       if HsGet.HTTPMethod('GET', AnsiString(request)) then begin
  12.          Resp.LoadFromStream(HsGet.Document);    
  13.          //Stringliste aufarbeiten
  14.          Result:= Trim(String(Resp.Text));
  15.       end else begin
  16.          Result:= 'Error';
  17.       end;
  18.    finally
  19.       GifView.Close;
  20.       Resp.Free;
  21.       HsGet.Free;
  22.    end;
  23. end;
  24.  

this does work but the Animation of the GIF will freeze when i wait for server response. I need something to show the user that something is happening. Can i somehow make the Form responsive / not freeze ? i tried using Application.ProccessMessages but that wasn't the trick. Could anyone pls help me out here ?
« Last Edit: May 30, 2024, 02:45:03 pm by Weitentaaal »

Khrys

  • Full Member
  • ***
  • Posts: 128
Re: Responsive Form with Animated GIF
« Reply #1 on: May 31, 2024, 07:40:41 am »
A general solution would be to spawn another thread:
Code: Pascal  [Select][+][-]
  1. type
  2.   PAsyncData = ^TAsyncData;
  3.   TAsyncData = record
  4.     Request, Response: String;
  5.     Ready: Cardinal;
  6.   end;
  7.  
  8. function GetHTTP_ThreadFunc(AsyncData: Pointer): PtrInt;
  9. var
  10.   Data: PAsyncData absolute AsyncData;
  11.   Client: THTTPSend = Nil;
  12.   Buffer: TStringList;
  13. begin
  14.   Data^.Response := 'Error';
  15.   Buffer := TStringList.Create();
  16.   try
  17.     Client := THTTPSend.Create();
  18.     if Client.HTTPMethod('GET', Data^.Request) then begin
  19.       Buffer.LoadFromStream(Client.Document);
  20.       Data^.Response := Buffer.Text.Trim();
  21.     end;
  22.   finally
  23.     Client.Free();
  24.   end;
  25.   InterlockedExchange(Data^.Ready, 1);
  26.   Buffer.Free();
  27.   Result := 0;
  28. end;
  29.  
  30. function GetHTTP(const Request: String): String;
  31. var
  32.   Data: TAsyncData;
  33. begin
  34.   Data := Default(TAsyncData);
  35.   Data.Request := Request;
  36.   BeginThread(@GetHTTP_ThreadFunc, @Data);
  37.   while InterlockedCompareExchange(Data.Ready, 0, 0) <> 1 do begin
  38.     Application.ProcessMessages();
  39.     Sleep(16);
  40.   end;
  41.   Result := Data.Response;
  42. end;

You'd then use it like this:

Code: Pascal  [Select][+][-]
  1. function GetResponse(request: WideString): String;
  2. begin
  3.   GifView.PathToGIF:= ExePath+'pics\ConnToServer.gif';
  4.   GifView.Show;
  5.   Result := GetHTTP(request);
  6.   GifView.Close;
  7. end;

The function is split in two: a frontend (GetHTTP) and a backend (GetHTTP_ThreadFunc) because spawning a thread requires a  TThreadFunc, which isn't ergonomic to use on its own. The  TAsyncData  record is needed for passing data between and synchronizing the threads, using atomic operations for the latter (not really necessary here, but good practice anyway).

While waiting for the request in the newly spawned thread to complete,  GetHTTP repeatedly calls  Application.ProcessMessages  so that the UI remains responsive, but I capped it at around 60 times per second (using  Sleep(16)), because doing it too often only drives up CPU usage with diminishing returns.

If you're doing lots of requests, you might want to use a thread pool to avoid the overhead of creating a thread every time.

Weitentaaal

  • Hero Member
  • *****
  • Posts: 550
Re: Responsive Form with Animated GIF
« Reply #2 on: May 31, 2024, 09:11:31 am »
thank you @Khrys ! this did work for me. im wont't have a lot of requests so i should be fine.

cdbc

  • Hero Member
  • *****
  • Posts: 1757
    • http://www.cdbc.dk
Re: Responsive Form with Animated GIF
« Reply #3 on: May 31, 2024, 09:57:51 am »
Hi
When doing threading this way(not joining the thread/waitfor) you must remember to put 'EndThread;' as the last line in your threadfunc, like this:
Code: Pascal  [Select][+][-]
  1. function GetHTTP_ThreadFunc(AsyncData: Pointer): PtrInt;
  2. var
  3.   Data: PAsyncData absolute AsyncData;
  4.   Client: THTTPSend = Nil;
  5.   Buffer: TStringList;
  6. begin
  7.   Data^.Response := 'Error';
  8.   Buffer := TStringList.Create();
  9.   try
  10.     Client := THTTPSend.Create();
  11.     if Client.HTTPMethod('GET', Data^.Request) then begin
  12.       Buffer.LoadFromStream(Client.Document);
  13.       Data^.Response := Buffer.Text.Trim();
  14.     end;
  15.   finally
  16.     Client.Free();
  17.   end;
  18.   InterlockedExchange(Data^.Ready, 1);
  19.   Buffer.Free();
  20.   Result := 0;
  21.   EndThread; { necessary when we're not joining the thread ~ Waitfor }
  22. end;
Also good practice  ;)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

 

TinyPortal © 2005-2018