Recent

Author Topic: [SOLVED] Expert needed: how to use 'Application.ShowMainForm:=false' correctly?  (Read 9037 times)

BrunoK

  • Hero Member
  • *****
  • Posts: 664
  • Retired programmer
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #30 on: December 16, 2024, 05:44:17 pm »
At OP Hartmut.

I don't understand what you expect to happen and in what order. The thing is you show a Modal From before having the application running and that seems, in Windows, to already activate the windows message queue.

I tried the following modification to the project code, see if it is nearer to what you expect.

Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.  cthreads,
  8.   {$ENDIF}
  9.   {$IFDEF HASAMIGA}
  10.  athreads,
  11.   {$ENDIF}
  12.   Interfaces, // this includes the LCL widgetset
  13.   Forms,
  14.   unit1,
  15.   Unit2 { you can add units after this };
  16.  
  17.   {$R *.res}
  18.  
  19. begin
  20.   // with the following code block it works only on Windows, but NOT on Linux:
  21.   RequireDerivedFormResource := True;
  22.   Application.Scaled := True;
  23.   Application.Initialize;
  24.   writeln('AAA');
  25.   writeln('BBB');
  26.   Form1 := TForm1.Create(Application); // Create Form1.Instance
  27.   Form1.FormActivate(Application); // Call Form2.ShowModal before activating Form1
  28.   if not Application.Terminated then begin
  29.     Form1.Show;
  30.     writeln('Call Application.Run');
  31.     Application.Run;
  32.   end
  33.   else
  34.     Application.Free;
  35. end.


alpine

  • Hero Member
  • *****
  • Posts: 1343
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #31 on: December 16, 2024, 05:48:32 pm »
This is not my code, this was created by jeremiah (in reply #5). But I think this is only a minor issue. The major issue is, that Application.Run() ist started too late, after the program has finished.
May i politely ask how the Application.Run() can be started after the program has finished? Seems impossible or misspelled.

It is not the best choice of code that OP suggested, but

Application.Terminate delays termination until Application.Run() processes the main thread's message queue.

Run the debugger, and you will see this result.
Yes, I know that well. The point is that neither Application.Terminate terminates the program immediately, neither the Application.Terminated means it is in fact "terminated". It is just that the TApplication.RunLoop loops until Terminated;

The real termination is at "end." (well, mostly). Stating that "something was started after the (whole)  program has finished" sounds just wrong.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #32 on: December 16, 2024, 07:00:17 pm »
Thanks to all for your many new posts. I will answer them peace-by-peace, but I am slow, because my English is not so fluid and I have to make tests according to your suggestions.

Hi
This:
Code: Pascal  [Select][+][-]
  1. procedure Test_Clipboard;
  2.    {abbreviation for to call 'Form2' from within 1 procedure}
  3. var Form2: TForm2;
  4. begin
  5.  Form2:=TForm2.CreateNew(Application);
  6.  Form2.Button1:=TButton.Create(Form2);
  7.  Form2.Button1.Parent:=Form2;
  8.  Form2.Button1.Caption:='Clipboard';
  9.  Form2.Button1.OnClick:=@Form2.Button1Click;
  10.  Form2.ShowModal;
  11.  Form2.Free;
  12. end;
Creates a form in 'pseudo-main-form' mode with its own 'showmodal' message-loop, behind the scenes it calls 'inherited Create();', and adds it to the forms list as the first one, a de-facto 'mainform'.
When that goes away, the application object continues creating 'Form1' until it realizes, it has been terminated and halts the application altogether...
Why would you do stuff like this in the visual apparatus?!?
Cannot for the life of me, see what you want to do...
/bc

The code in my very first post is a small extract of my real project. Just enough to demonstate the problem. Procedure Test_Clipboard is a placeholder for all the tasks, which the "common unit" can do. Because only the clipboard failed, I shortened the whole common unit to do only this task.

This is not my code, this was created by jeremiah (in reply #5). But I think this is only a minor issue. The major issue is, that Application.Run() ist started too late, after the program has finished.
May i politely ask how the Application.Run() can be started after the program has finished? Seems impossible or misspelled.

Oh - I understand. I formulated this mistakable. Sorry, my fault. Thank you alpine for asking!

What I meant is: my program from my 1st post has a "main task" which is procedure Test_Clipboard. In this procedure the program waits for a button, then saves something into the clipboard and then you can exit the program via the "X" in the upper right corner of Form2. This moment did I mean with "the program has finished". The issue with Application.Run() is, that Application.Run() is only started, *after* you exited Form2 via the "X" in the upper right corner. But then the "main task" had already finished. So the "main task" could not benefit from the loop in Application.Run(). Hope this is better understandable now.

Hi
Oh, it's true, that's because of his *Shenanigans* in the 'FormCreate' handler, it gets all screwed to h*ll and ends up finishing before 'Run'.
It's a *Shure-fire* way to get burnt, when playing with fire...  :D
There's a lot going on in the LCL, pretty much from the 'getgo', one should treat it with some respect.
/bc

Sorry, I don't understand this, even with a translator...

I think the same effect can be achieved by simply setting Form1.Visible to False and without any modification of the project main file. May have got the intent wrong though.

Setting Form1.Visible to False does not work in FormCreate(). It only works in FormActivate(). And this produces a short flicker, which I wanted to avoid by using 'Application.ShowMainForm:=false'.

Need to stop for today. Will continue tomorrow. Thanks to all.

alpine

  • Hero Member
  • *****
  • Posts: 1343
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #33 on: December 16, 2024, 07:15:24 pm »
Quote
Setting Form1.Visible to False does not work in FormCreate(). It only works in FormActivate(). And this produces a short flicker, which I wanted to avoid by using 'Application.ShowMainForm:=false'.
Try to set it invisible into the Designer. Not in FormCreate().
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

wp

  • Hero Member
  • *****
  • Posts: 12589
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #34 on: December 16, 2024, 08:06:28 pm »
I think the real problem is that you do not want to call Application.Run.

Then we misunderstood. I do call Application.Run().
I was confused because you had commented it out in the first post.

This is my current code in the project file (taken from reply #8):

Code: Pascal  [Select][+][-]
  1. begin
  2.  RequireDerivedFormResource:=True;
  3.  Application.Scaled:=True;
  4.  Application.Initialize;
  5. writeln('AAA');
  6.  Application.CreateForm(TForm1, Form1);
  7. writeln('BBB');
  8.  Application.ShowMainForm:=false;
  9. writeln('CCC');
  10.  Application.Run;
  11. writeln('DDD');
  12. end.
I stepped through these lines with the debugger and found that the code enters Application.CreateForm from where it does not exit until the Form2 is closed. Therefore you don't see the following print messages. But that's clear because Form2 is shown modally.

Diving into Application.CreateForm reveals that the modal form is shown very very early before anything has had a chance to setup properly. Basically this is because Form2 is created and shown in the OnCreate handler. Buth rather than doing this immediately in this event handler you should delay its execution. A convenient way to do this is calling the QueueAsyncCall method of the Application - it just puts the request to create and show into the message queue and lets the program continue until the Run method entered and the message queue command are handled. Now everything works correctly (in my opinion).

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   Application.QueueAsyncCall(@ShowForm2, 0);
  4. end;
  5.  
  6. procedure TForm1.ShowForm2(Data: PtrInt);  // This is the signature needed by QueueAsyncCall
  7. begin
  8.   Unit2.Test_Clipboard; {saves a graphic file into the clipboard}
  9.   Application.Terminate;
  10. end;
  11.  
« Last Edit: December 16, 2024, 08:08:44 pm by wp »

BrunoK

  • Hero Member
  • *****
  • Posts: 664
  • Retired programmer
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #35 on: December 16, 2024, 10:45:56 pm »
Essentially what I commented in line
Code: Pascal  [Select][+][-]
  1. Form1.FormActivate(Application); // Call Form2.ShowModal before activating Form1

jeremiah

  • New Member
  • *
  • Posts: 18
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #36 on: December 17, 2024, 01:47:55 am »
I remembered today of a colleague years ago using {$APPTYPE CONSOLE} in Delphi to hide the main form.  Visible still needs to be false and you can to away with Application.ShowMainForm in the .lpr. Maybe this will help??

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #37 on: December 17, 2024, 01:27:55 pm »
I don't understand what you expect to happen and in what order. The thing is you show a Modal From before having the application running and that seems, in Windows, to already activate the windows message queue.

Maybe this is the reason, why the clipboard works on Windows and not on Linux.

Quote
I tried the following modification to the project code, see if it is nearer to what you expect.

Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.  cthreads,
  8.   {$ENDIF}
  9.   {$IFDEF HASAMIGA}
  10.  athreads,
  11.   {$ENDIF}
  12.   Interfaces, // this includes the LCL widgetset
  13.   Forms,
  14.   unit1,
  15.   Unit2 { you can add units after this };
  16.  
  17.   {$R *.res}
  18.  
  19. begin
  20.   // with the following code block it works only on Windows, but NOT on Linux:
  21.   RequireDerivedFormResource := True;
  22.   Application.Scaled := True;
  23.   Application.Initialize;
  24.   writeln('AAA');
  25.   writeln('BBB');
  26.   Form1 := TForm1.Create(Application); // Create Form1.Instance
  27.   Form1.FormActivate(Application); // Call Form2.ShowModal before activating Form1
  28.   if not Application.Terminated then begin
  29.     Form1.Show;
  30.     writeln('Call Application.Run');
  31.     Application.Run;
  32.   end
  33.   else
  34.     Application.Free;
  35. end.

Thanks for your suggestion, I tried it. Now the clipboard works on Linux, but Application.Run() is started again too late: The whole "main task" is done in Unit2.Test_Clipboard(), which is called from inside 'Form1.FormActivate(Application)', which will only return, *after' the "main task" had been finished by the "X" in the upper right corner of Form2. The issue with Application.Run() is, that Application.Run() is only started, *after* you exited Form2. But then the "main task" had already finished. So the "main task" could not benefit from the loop in Application.Run().

I hesitate to rely on this solution, because the "main task" runs completely without the loop from Application.Run() and I fear for unpredictable side affects running a GUI program without Application.Run().



Quote
Setting Form1.Visible to False does not work in FormCreate(). It only works in FormActivate(). And this produces a short flicker, which I wanted to avoid by using 'Application.ShowMainForm:=false'.
Try to set it invisible into the Designer. Not in FormCreate().

I had tried this already before, but unfortunately it does not work. Would have been the easiest solution...



I remembered today of a colleague years ago using {$APPTYPE CONSOLE} in Delphi to hide the main form.  Visible still needs to be false and you can to away with Application.ShowMainForm in the .lpr. Maybe this will help??

The purpose of {$APPTYPE CONSOLE} is to enable a console output on Windows. On Linux it instead invokes a compiler warning "Warning: APPTYPE is not supported by the target OS".



Diving into Application.CreateForm reveals that the modal form is shown very very early before anything has had a chance to setup properly. Basically this is because Form2 is created and shown in the OnCreate handler. Buth rather than doing this immediately in this event handler you should delay its execution. A convenient way to do this is calling the QueueAsyncCall method of the Application - it just puts the request to create and show into the message queue and lets the program continue until the Run method entered and the message queue command are handled. Now everything works correctly (in my opinion).

Thanks a lot wp for this suggestion. The good news is: Application.Run() is now started early enough. The bad news is: the clipboard on Linux does however not work. Instead it writes the same error message on the console as before. This is the complete console output:

Code: Text  [Select][+][-]
  1. AAA
  2. FormCreate
  3. BBB
  4. CCC
  5. ShowForm2
  6. (project1:10240): Gtk-CRITICAL **: 12:28:05.193: IA__gtk_selection_owner_set: assertion 'widget == NULL || gtk_widget_get_realized (widget)' failed
  7. DDD

This is the new main unit:

Code: Pascal  [Select][+][-]
  1. unit Unit1; {main unit}
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, unit2;
  9.  
  10. type
  11.  
  12.  { TForm1 }
  13.  
  14.  TForm1 = class(TForm)
  15.   procedure FormCreate(Sender: TObject);
  16.   procedure ShowForm2(Data: PtrInt);
  17.  private
  18.  public
  19.  end;
  20.  
  21. var
  22.  Form1: TForm1;
  23.  
  24. implementation
  25.  
  26. {$R *.lfm}
  27.  
  28. { TForm1 }
  29.  
  30. procedure TForm1.FormCreate(Sender: TObject);
  31.    begin
  32.    writeln('FormCreate');
  33.    Application.QueueAsyncCall(@ShowForm2, 0);
  34.    end;
  35.  
  36. procedure TForm1.ShowForm2(Data: PtrInt); // This is the signature needed by QueueAsyncCall
  37. begin
  38.   writeln('ShowForm2');
  39.   Unit2.Test_Clipboard; {saves a graphic file into the clipboard}
  40.   Application.Terminate;
  41. end;
  42.  
  43. end.

I attached the updated project (including an updated project file) for you.
Can it be that there is a bug in the gtk2 clipboard or it's initialization ???

Versions: Linux Ubuntu 22.04 64-bit with Lazarus 3.4.0 gtk2.

cdbc

  • Hero Member
  • *****
  • Posts: 1808
    • http://www.cdbc.dk
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #38 on: December 17, 2024, 01:46:04 pm »
Hi
Sorry to have to say this: Your architecture is completely shijt wrong! You're figthing the LCL instead of working with it.
Rethink it and don't do everything in the visual mechanism...
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

alpine

  • Hero Member
  • *****
  • Posts: 1343
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #39 on: December 17, 2024, 01:56:16 pm »
Hi
Sorry to have to say this: Your architecture is completely shijt wrong! You're figthing the LCL instead of working with it.
Rethink it and don't do everything in the visual mechanism...
Regards Benny
+1

Quote
I had tried this already before, but unfortunately it does not work. Would have been the easiest solution...
How about calling it directly:
Code: Pascal  [Select][+][-]
  1. begin
  2. // with the following code block it works only on Windows, but NOT on Linux:
  3.  RequireDerivedFormResource:=True;
  4.  Application.Scaled:=True;
  5.  Application.Initialize;
  6.  //Application.ShowMainForm:=false; {used to hide the main form}
  7. //writeln('AAA');
  8. // Application.CreateForm(TForm1, Form1); {calls FormCreate()}
  9. //writeln('BBB');
  10. // Form1.FormActivate(nil); {is not longer called automatically}
  11. //writeln('CCC');
  12. // Application.Run;       {would be called only AFTER the program has ended}
  13. //writeln('DDD');
  14.   Test_Clipboard;
  15. end.

Why the hell would you want to put a modal form? Modal only makes sense if you have more than one form in the application.
« Last Edit: December 17, 2024, 02:04:15 pm by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

wp

  • Hero Member
  • *****
  • Posts: 12589
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #40 on: December 17, 2024, 02:46:50 pm »
The bad news is: the clipboard on Linux does however not work. Instead it writes the same error message on the console as before.
Yes, I can confirm this now on Manjaro Linux, but only with gtk2 widgetset, qt5 and qt6 are working correctly (like Windows).

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #41 on: December 17, 2024, 03:12:11 pm »
Hi
Sorry to have to say this: Your architecture is completely shijt wrong! You're figthing the LCL instead of working with it.
Rethink it and don't do everything in the visual mechanism...
Regards Benny

I totally agree with you, that the architectures in this Topic are not recommendable. They are only the result of multiple attempts from friendly helpers to try to get feature 'Application.ShowMainForm:=false' to work. Seems that nobody knows how to do that. I'm still hoping for an answer of one of the LCL Developers who knows that.



How about calling it directly:
Code: Pascal  [Select][+][-]
  1. begin
  2.  RequireDerivedFormResource:=True;
  3.  Application.Scaled:=True;
  4.  Application.Initialize;
  5. ...
  6.   Test_Clipboard;
  7. end.

This way I would loose the Main Form completely. Of course then 'Application.ShowMainForm:=false' would not be needed any longer... But I wrote multiple times, that I need the Main Form.

Quote
Why the hell would you want to put a modal form? Modal only makes sense if you have more than one form in the application.

As I wrote in my very 1st post and in reply #32, unit2.Test_Clipboard() is only a placeholder for all the tasks, which my "common unit" can do (because only the clipboard failed, I shortened the whole common unit to do only this task). This common unit has multiple Forms and the "main form" of the common unit is modal.



The bad news is: the clipboard on Linux does however not work. Instead it writes the same error message on the console as before.
Yes, I can confirm this now on Manjaro Linux, but only with gtk2 widgetset, qt5 and qt6 are working correctly (like Windows).

Thank you very much for testing. The question now is: does the gtk2 clipboard fail, because it is called or initialized in a wrong way or is there a bug? Is there a workaround for that?

BrunoK

  • Hero Member
  • *****
  • Posts: 664
  • Retired programmer
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #42 on: December 17, 2024, 04:55:03 pm »
Try this one.

Output :
Quote
FormCreate
Application.Run
TForm1.ShowForm2(Data: PtrInt)
TForm1.Visible : FALSE
Form2.ShowModal
Delayed Form1.Show
TForm1.Visible : TRUE
TForm1.FormShow(Sender: TObject) : TForm1 Form1

LV

  • Full Member
  • ***
  • Posts: 204
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #43 on: December 17, 2024, 05:01:34 pm »
I am not an expert, so my response will be based on your example since I am unfamiliar with your real project.

Requirements:
1. The main form should be invisible.
2. The second form must be modal and include a button to copy the image to the clipboard.

Objective:
Create an application compatible with both Windows and Linux Deb64 GTK2.

Additional Condition: Do not use 'Application.ShowMainForm := false'.

Solution: Please see the attached project.

Please don’t judge me too harshly; I spent a little time on this, so I may have overlooked something.

BrunoK

  • Hero Member
  • *****
  • Posts: 664
  • Retired programmer
Re: Expert needed: how to use 'Application.ShowMainForm:=false' correctly?
« Reply #44 on: December 17, 2024, 05:16:57 pm »
LV : your solution is simple and elegant, the RunLoop executes and if OP wants form1 to be shown all there is to do is to Form1.Show instead of Application.Terminate.

Seems to be what the OP wanted to achieve.

 

TinyPortal © 2005-2018