Recent

Author Topic: [SOLVED] Create Gui app  (Read 1214 times)

Pe3s

  • Hero Member
  • *****
  • Posts: 641
[SOLVED] Create Gui app
« on: February 25, 2025, 12:45:27 pm »
Hello forumers I have started writing a gui overlay for tailwind standalone cli. I have encountered a problem that I cannot solve, namely how to read the logs of a hidden console e.g. in the Memo 1 component?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  9.   Process;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Button2: TButton;
  18.     Edit1: TEdit;
  19.     Edit2: TEdit;
  20.     MemoOutput: TMemo;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure Button2Click(Sender: TObject);
  23.   private
  24.     TailwindProcess: TProcess;
  25.  
  26.     procedure StartTailwindWatch;
  27.     procedure StopTailwindWatch;
  28.  
  29.   public
  30.  
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. { TForm1 }
  41.  
  42. procedure TForm1.Button1Click(Sender: TObject);
  43. begin
  44.   StartTailwindWatch;
  45. end;
  46.  
  47. procedure TForm1.Button2Click(Sender: TObject);
  48. begin
  49.   StopTailwindWatch;
  50. end;
  51.  
  52. procedure TForm1.StartTailwindWatch;
  53. begin
  54.   TailwindProcess := TProcess.Create(nil);
  55.   try
  56.     TailwindProcess.Executable := 'tailwindcss-windows-x64.exe';
  57.     TailwindProcess.Parameters.Add('-i');
  58.     TailwindProcess.Parameters.Add('style.css');
  59.     TailwindProcess.Parameters.Add('-o');
  60.     TailwindProcess.Parameters.Add('output.css');
  61.     TailwindProcess.Parameters.Add('--watch');
  62.     TailwindProcess.Options := [poNoConsole];
  63.     TailwindProcess.Execute;
  64.   except
  65.     on E: Exception do
  66.       ShowMessage('Błąd uruchamiania Tailwind CSS: ' + E.Message);
  67.   end;
  68. end;
  69.  
  70. procedure TForm1.StopTailwindWatch;
  71. begin
  72.   if Assigned(TailwindProcess) then
  73.   begin
  74.     TailwindProcess.Terminate(0);
  75.     TailwindProcess.Free;
  76.     TailwindProcess := nil;
  77.   end;
  78. end;
  79.  
  80.  
  81.  
  82. end.
  83.  
  84.  
« Last Edit: February 27, 2025, 09:27:15 pm by Pe3s »

ttomas

  • Full Member
  • ***
  • Posts: 248
Re: Create Gui app
« Reply #1 on: February 25, 2025, 02:18:44 pm »
You start tailwind with --watch param! That mean your process  will not end and you can't use TailwindProcess.Output.
Try with TAsyncProcess, more info
https://lazarus-ccr.sourceforge.io/docs/lcl/asyncprocess/tasyncprocess.html

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12708
  • FPC developer.
Re: Create Gui app
« Reply #2 on: February 25, 2025, 02:31:32 pm »
To log process output to a memo in FPC 3.2.0+ look at https://forum.lazarus.freepascal.org/index.php/topic,53716.30.html

Most of the older TUTF8Process etc variants are now obsolete.

Pe3s

  • Hero Member
  • *****
  • Posts: 641
Re: Create Gui app
« Reply #3 on: February 25, 2025, 05:27:42 pm »
I have corrected the code but it still does not display what is in the console.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  9.   Buttons, Process, AsyncProcess;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Button2: TButton;
  18.     Edit1: TEdit;
  19.     Edit2: TEdit;
  20.     ImageList1: TImageList;
  21.     Memo1: TMemo;
  22.     SpeedButton1: TSpeedButton;
  23.     Timer1: TTimer;
  24.     procedure Button1Click(Sender: TObject);
  25.     procedure Button2Click(Sender: TObject);
  26.     procedure Timer1Timer(Sender: TObject);
  27.   private
  28.     TailwindProcess: TAsyncProcess;
  29.  
  30.     procedure StartTailwindWatch;
  31.     procedure StopTailwindWatch;
  32.     procedure OnTailwindProcessTerminate(Sender: TObject);
  33.  
  34.   public
  35.  
  36.   end;
  37.  
  38. var
  39.   Form1: TForm1;
  40.  
  41. implementation
  42.  
  43. {$R *.lfm}
  44.  
  45. { TForm1 }
  46.  
  47. procedure TForm1.Button1Click(Sender: TObject);
  48. begin
  49.   StartTailwindWatch;
  50. end;
  51.  
  52. procedure TForm1.Button2Click(Sender: TObject);
  53. begin
  54.   StopTailwindWatch;
  55. end;
  56.  
  57. procedure TForm1.Timer1Timer(Sender: TObject);
  58. var
  59.   BytesAvailable: Integer;
  60.   Buffer: string;
  61.   ReadCount: Integer;
  62. begin
  63.   if Assigned(TailwindProcess) and TailwindProcess.Running then
  64.   begin
  65.     BytesAvailable := TailwindProcess.Output.NumBytesAvailable;
  66.     if BytesAvailable > 0 then
  67.     begin
  68.       SetLength(Buffer, BytesAvailable);
  69.       ReadCount := TailwindProcess.Output.Read(Buffer[1], BytesAvailable);
  70.       if ReadCount > 0 then
  71.       begin
  72.         Memo1.Lines.Add(Buffer);
  73.         Memo1.SelStart := Length(Memo1.Text); // Auto-scroll
  74.         Application.ProcessMessages; // Ensure UI updates in real-time
  75.       end;
  76.     end;
  77.   end;
  78.  
  79. end;
  80.  
  81. procedure TForm1.StartTailwindWatch;
  82. begin
  83.   if Assigned(TailwindProcess) then
  84.     Exit; // Prevent multiple instances
  85.  
  86.   TailwindProcess := TAsyncProcess.Create(nil);
  87.   try
  88.     TailwindProcess.Executable := 'tailwindcss-windows-x64.exe';
  89.     TailwindProcess.Parameters.Add('-i');
  90.     TailwindProcess.Parameters.Add('style.css');
  91.     TailwindProcess.Parameters.Add('-o');
  92.     TailwindProcess.Parameters.Add('output.css');
  93.     TailwindProcess.Parameters.Add('--watch');
  94.     TailwindProcess.Options := [poUsePipes];  // Enable reading output
  95.  
  96.     TailwindProcess.OnTerminate := @OnTailwindProcessTerminate;
  97.  
  98.     TailwindProcess.Execute;
  99.  
  100.     // Enable timer to read logs
  101.     Timer1.Interval := 300; // Adjust as needed
  102.     Timer1.Enabled := True;
  103.   except
  104.     on E: Exception do
  105.     begin
  106.       ShowMessage('Błąd uruchamiania Tailwind CSS: ' + E.Message);
  107.       FreeAndNil(TailwindProcess);
  108.     end;
  109.   end;
  110. end;
  111.  
  112. procedure TForm1.StopTailwindWatch;
  113. begin
  114.    if Assigned(TailwindProcess) then
  115.   begin
  116.     TailwindProcess.Terminate(0);
  117.     FreeAndNil(TailwindProcess);
  118.     Timer1.Enabled := False;
  119.   end;
  120. end;
  121.  
  122. procedure TForm1.OnTailwindProcessTerminate(Sender: TObject);
  123. begin
  124.    Memo1.Lines.Add('Tailwind CSS process zakończył działanie.');
  125.   FreeAndNil(TailwindProcess);
  126.   Timer1.Enabled := False;
  127. end;
  128.  
  129.  
  130.  
  131. end.
  132.  
  133.  

Pe3s

  • Hero Member
  • *****
  • Posts: 641
Re: Create Gui app
« Reply #4 on: February 26, 2025, 12:31:15 pm »
How to capture the console and display it in memo?

dseligo

  • Hero Member
  • *****
  • Posts: 1672
Re: Create Gui app
« Reply #5 on: February 26, 2025, 12:36:49 pm »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12708
  • FPC developer.
Re: Create Gui app
« Reply #6 on: February 26, 2025, 12:39:14 pm »
Some applications try to detect consoles and behave differently.

Try to use a different application and see if that works correctly to verify that the mechanism is correct (and it seems to me it is correct if the process doesn't also write to stderr)

Pe3s

  • Hero Member
  • *****
  • Posts: 641
Re: Create Gui app
« Reply #7 on: February 26, 2025, 04:24:42 pm »
I changed the component to synedit, added stderr and now nothing is displayed in both the console and the window. What do I need to fix?
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  9.   Buttons, Process, AsyncProcess, SynEdit;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     CheckBox1: TCheckBox;
  17.     Edit1: TEdit;
  18.     Edit2: TEdit;
  19.     ImageList1: TImageList;
  20.     SpeedButton1: TSpeedButton;
  21.     SynEdit1: TSynEdit;
  22.     procedure SpeedButton1Click(Sender: TObject);
  23.   private
  24.     TailwindProcess: TAsyncProcess;
  25.  
  26.     procedure StartTailwindWatch;
  27.     procedure StopTailwindWatch;
  28.     procedure OnTailwindProcessTerminate(Sender: TObject);
  29.     procedure OnTailwindProcessOutput(Sender: TObject);
  30.  
  31.   public
  32.  
  33.   end;
  34.  
  35. var
  36.   Form1: TForm1;
  37.   FRuning: Boolean;
  38.  
  39. implementation
  40.  
  41. {$R *.lfm}
  42.  
  43. { TForm1 }
  44.  
  45. procedure TForm1.SpeedButton1Click(Sender: TObject);
  46. begin
  47.   FRuning := not FRuning;
  48.   if FRuning then
  49.     StartTailwindWatch
  50.   else
  51.     StopTailwindWatch;
  52.  
  53.   SpeedButton1.ImageIndex := Ord(FRuning);
  54. end;
  55.  
  56. procedure TForm1.StartTailwindWatch;
  57. begin
  58.   if Assigned(TailwindProcess) then
  59.     Exit; // Zapobiega wielokrotnemu uruchomieniu
  60.  
  61.   if not FileExists('tailwindcss-windows-x64.exe') then
  62.   begin
  63.     ShowMessage('Plik tailwindcss-windows-x64.exe nie został znaleziony!');
  64.     Exit;
  65.   end;
  66.  
  67.   TailwindProcess := TAsyncProcess.Create(nil);
  68.   try
  69.     TailwindProcess.Executable := 'tailwindcss-windows-x64.exe';
  70.     TailwindProcess.Parameters.Add('-i');
  71.     TailwindProcess.Parameters.Add('style.css');
  72.     TailwindProcess.Parameters.Add('-o');
  73.     TailwindProcess.Parameters.Add('output.css');
  74.     TailwindProcess.Parameters.Add('--watch');
  75.     if CheckBox1.Checked then
  76.     begin
  77.       TailwindProcess.Parameters.Add('-m');
  78.     end;
  79.  
  80.     TailwindProcess.Options := [poUsePipes, poNoConsole, poNewConsole];
  81.  
  82.     // Set event handler for when the process terminates
  83.     TailwindProcess.OnTerminate := @OnTailwindProcessTerminate;
  84.     TailwindProcess.OnReadData := @OnTailwindProcessOutput;
  85.  
  86.     TailwindProcess.Execute;
  87.   except
  88.     on E: Exception do
  89.     begin
  90.       ShowMessage('Błąd uruchamiania Tailwind CSS: ' + E.Message);
  91.       FreeAndNil(TailwindProcess);
  92.     end;
  93.   end;
  94. end;
  95.  
  96. procedure TForm1.StopTailwindWatch;
  97. begin
  98.   if Assigned(TailwindProcess) then
  99.   begin
  100.     TailwindProcess.Terminate(0);
  101.     FreeAndNil(TailwindProcess);
  102.   end;
  103. end;
  104.  
  105. procedure TForm1.OnTailwindProcessTerminate(Sender: TObject);
  106. begin
  107.   ShowMessage('Tailwind CSS process zakończył działanie.');
  108.   FreeAndNil(TailwindProcess);
  109.   SpeedButton1.ImageIndex := 0;
  110.   FRuning := False;
  111. end;
  112.  
  113. procedure TForm1.OnTailwindProcessOutput(Sender: TObject);
  114. var
  115.   OutputString, ErrorString: string;
  116. begin
  117.   OutputString := '';
  118.   ErrorString := '';
  119.  
  120.   // Read standard output
  121.   if TailwindProcess.Output.NumBytesAvailable > 0 then
  122.   begin
  123.     SetLength(OutputString, TailwindProcess.Output.NumBytesAvailable);
  124.     TailwindProcess.Output.Read(OutputString[1], Length(OutputString));
  125.     SynEdit1.Lines.Add(OutputString);
  126.   end;
  127.  
  128.   // Read standard error
  129.   if TailwindProcess.Stderr.NumBytesAvailable > 0 then
  130.   begin
  131.     SetLength(ErrorString, TailwindProcess.Stderr.NumBytesAvailable);
  132.     TailwindProcess.Stderr.Read(ErrorString[1], Length(ErrorString));
  133.     SynEdit1.Lines.Add(ErrorString);
  134.   end;
  135. end;
  136.  
  137. end.
  138.  
  139.  

paweld

  • Hero Member
  • *****
  • Posts: 1571
Re: Create Gui app
« Reply #8 on: February 27, 2025, 07:20:54 am »
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, process, Forms, Controls, Graphics, Dialogs, StdCtrls, UTF8Process;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Memo1: TMemo;
  17.     procedure Button1Click(Sender: TObject);
  18.   private
  19.     function RemoveCmdDecoration(s: String): String;
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.Button1Click(Sender: TObject);
  34. const
  35.   READ_BYTES = 2048;
  36. var
  37.   ptwc: TProcessUTF8;
  38.   i, BytesRead, NumBytes, LastOutputCount: Integer;
  39.   ms: TMemoryStream;
  40.   s: String;
  41. begin
  42.   Memo1.Lines.Clear;
  43.   ms := TMemoryStream.Create;
  44.   BytesRead := 0;
  45.   LastOutputCount := 0;
  46.   s := '';
  47.   ptwc := TProcessUTF8.Create(nil);
  48.   ptwc.Options := [poUsePipes, poStderrToOutPut, poNoConsole];
  49.   ptwc.Executable := 'tailwindcss-windows-x64.exe';
  50.   ptwc.Parameters.Add('-i');
  51.   ptwc.Parameters.Add('in.css');
  52.   ptwc.Parameters.Add('-o');
  53.   ptwc.Parameters.Add('out.css');
  54.   ptwc.Parameters.Add('-m');
  55.   ptwc.Execute;
  56.   while ptwc.Running or (NumBytes > 0) do
  57.   begin
  58.     ms.SetSize(BytesRead + READ_BYTES);
  59.     NumBytes := ptwc.Output.Read((ms.Memory + BytesRead)^, READ_BYTES);
  60.     if NumBytes > 0 then
  61.     begin
  62.       Inc(BytesRead, NumBytes);
  63.       SetString(s, ms.Memory, BytesRead);
  64.       s := Copy(s, LastOutputCount + 1, BytesRead - LastOutputCount);
  65.       LastOutputCount := LastOutputCount + Length(s);
  66.       Memo1.Lines.Add(RemoveCmdDecoration(s));
  67.     end;
  68.   end;
  69.   ptwc.Free;
  70.   ms.Free;
  71. end;
  72.  
  73. function TForm1.RemoveCmdDecoration(s: String): String;
  74. var
  75.   i: Integer;
  76.   isdeco: Boolean;
  77. begin
  78.   if pos(#27, s) = 0 then
  79.   begin
  80.     Result := s;
  81.     exit;
  82.   end;      
  83.   Result := '';
  84.   isdeco := False;
  85.   for i := 1 to Length(s) do
  86.   begin
  87.     if not isdeco then
  88.     begin
  89.       if s[i] = #27 then
  90.         isdeco := True
  91.       else
  92.         Result := Result + s[i];
  93.     end
  94.     else if s[i] = 'm' then
  95.       isdeco := False;
  96.   end;
  97. end;
  98.  
  99. end.
  100.  
Best regards / Pozdrawiam
paweld

Pe3s

  • Hero Member
  • *****
  • Posts: 641
Re: Create Gui app
« Reply #9 on: February 27, 2025, 09:26:41 pm »
Thank you for your help. / Dziekuje  :)
Regards / Pozdrawiam

 

TinyPortal © 2005-2018