Forum > LCL

Responsive Form with Animated GIF

(1/1)

Weitentaaal:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- {$R *.lfm} function SetLayeredWindowAttributes(hWnd: longint; Color: longint;   X: byte; alpha: longint): bool stdcall; external 'USER32'; function SetWindowLongA(hWnd: longint; nIndex: longint;   dwNewLong: longint): longint stdcall; external 'USER32'; function GetWindowLongA(hWnd: longint; nIndex: longint): longint stdcall;   external 'user32'; procedure SetTranslucent(ThehWnd: longint; Color: longint; nTrans: integer);var   Attrib: longint;begin   {SetWindowLong and SetLayeredWindowAttributes = API functions}   Attrib := GetWindowLongA(ThehWnd, GWL_EXSTYLE);   SetWindowLongA(ThehWnd, GWL_EXSTYLE, attrib or WS_EX_LAYERED);   {anything with color value color will completely disappear if flag = 1 or flag = 3  }   SetLayeredWindowAttributes(ThehWnd, Color, nTrans, 1);end; { TGifView } procedure TGifView.FormShow(Sender: TObject);begin   Image.Picture.LoadFromFile(PathToGIF);    self.Top:= Application.MainForm.Top + Application.MainForm.Height - Self.Height - 10;   self.Left:= Application.MainForm.Left + Application.MainForm.Width - Self.Width - 10;end; procedure TGifView.FormCreate(Sender: TObject);var   Transparency: longint;begin   Self.Color := clRed;   Transparency := Self.Color;   SetTranslucent(Self.Handle, Transparency, 0);    PathToGIF:= '';end;  
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function GetResponse(request: WideString): String;var   HsGet: THTTPSend;   Resp: TStringList;begin   HsGet:= THTTPSend.Create;   Resp:= TStringList.Create;   GifView.PathToGIF:= ExePath+'pics\ConnToServer.gif';   GifView.Show;   try      if HsGet.HTTPMethod('GET', AnsiString(request)) then begin         Resp.LoadFromStream(HsGet.Document);             //Stringliste aufarbeiten         Result:= Trim(String(Resp.Text));      end else begin         Result:= 'Error';      end;   finally      GifView.Close;      Resp.Free;      HsGet.Free;   end;end; 
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 ?

Khrys:
A general solution would be to spawn another thread:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  PAsyncData = ^TAsyncData;  TAsyncData = record    Request, Response: String;    Ready: Cardinal;  end; function GetHTTP_ThreadFunc(AsyncData: Pointer): PtrInt;var  Data: PAsyncData absolute AsyncData;  Client: THTTPSend = Nil;  Buffer: TStringList;begin  Data^.Response := 'Error';  Buffer := TStringList.Create();  try    Client := THTTPSend.Create();    if Client.HTTPMethod('GET', Data^.Request) then begin      Buffer.LoadFromStream(Client.Document);      Data^.Response := Buffer.Text.Trim();    end;  finally    Client.Free();  end;  InterlockedExchange(Data^.Ready, 1);  Buffer.Free();  Result := 0;end; function GetHTTP(const Request: String): String;var  Data: TAsyncData;begin  Data := Default(TAsyncData);  Data.Request := Request;  BeginThread(@GetHTTP_ThreadFunc, @Data);  while InterlockedCompareExchange(Data.Ready, 0, 0) <> 1 do begin    Application.ProcessMessages();    Sleep(16);  end;  Result := Data.Response;end;
You'd then use it like this:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function GetResponse(request: WideString): String;begin  GifView.PathToGIF:= ExePath+'pics\ConnToServer.gif';  GifView.Show;  Result := GetHTTP(request);  GifView.Close;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:
thank you @Khrys ! this did work for me. im wont't have a lot of requests so i should be fine.

cdbc:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function GetHTTP_ThreadFunc(AsyncData: Pointer): PtrInt;var  Data: PAsyncData absolute AsyncData;  Client: THTTPSend = Nil;  Buffer: TStringList;begin  Data^.Response := 'Error';  Buffer := TStringList.Create();  try    Client := THTTPSend.Create();    if Client.HTTPMethod('GET', Data^.Request) then begin      Buffer.LoadFromStream(Client.Document);      Data^.Response := Buffer.Text.Trim();    end;  finally    Client.Free();  end;  InterlockedExchange(Data^.Ready, 1);  Buffer.Free();  Result := 0;  EndThread; { necessary when we're not joining the thread ~ Waitfor }end;Also good practice  ;)
Regards Benny

Navigation

[0] Message Index

Go to full version