Recent

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

Hartmut

  • Hero Member
  • *****
  • Posts: 891
I changed the subject of this Topic in the hope to find someone, who knows what is the correct (intended) use of feature 'Application.ShowMainForm:=false'. Please see reply #14 and the end of reply #18 for a short summary. Thanks a lot.



I have written a common unit to display a Graphic file and some more ... and to save it into the Clipboard. Now I want to use this common unit in a program, which means, that I don't need the Main Form of the program, because the common unit provides its own Main Form. So I wanted to hide the Main Form of the program (which flickers) by

Code: Pascal  [Select][+][-]
  1. Application.ShowMainForm:=false;

Everything works perfectly on Windows and Linux except for 1 problem: when I save a graphic file into the Clipboard, then this works only on Windows, but on Linux instead the following message is written to the console:

(project1:3656): Gtk-CRITICAL **: 16:40:01.613: IA__gtk_selection_owner_set: assertion 'widget == NULL || gtk_widget_get_realized (widget)' failed

But when I start the program the "normal way" (without 'Application.ShowMainForm:=false') then saving to the clipboard works on Linux too.

So my question is: what did I wrong in the kind of using 'Application.ShowMainForm:=false'?

I attached a compilable demo project including a demo graphic file:

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, unit1, Unit2
  14.  { you can add units after this };
  15.  
  16. {$R *.res}
  17.  
  18. begin
  19. // with the following code block it works on Windows AND Linux:
  20. (*
  21.  RequireDerivedFormResource:=True;
  22.  Application.Scaled:=True;
  23.  Application.Initialize;
  24.  Application.CreateForm(TForm1, Form1);
  25.  Application.Run;
  26. *)
  27.  
  28. // with the following code block it works only on Windows, but NOT on Linux:
  29.  RequireDerivedFormResource:=True;
  30.  Application.Scaled:=True;
  31.  Application.Initialize;
  32.  Application.ShowMainForm:=false; {used to hide the main form}
  33. writeln('AAA');
  34.  Application.CreateForm(TForm1, Form1); {calls FormCreate()}
  35. writeln('BBB');
  36.  Form1.FormActivate(nil); {is not longer called automatically}
  37. writeln('CCC');
  38. // Application.Run;       {would be called only AFTER the program has ended}
  39. writeln('DDD');
  40. end.

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 FormActivate(Sender: TObject);
  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.    end;
  34.  
  35. procedure TForm1.FormActivate(Sender: TObject);
  36.    const first: boolean = true;
  37.    begin
  38.    writeln('FormActivate');
  39.    if not first then exit;
  40.  
  41.    first:=false;
  42.    self.Hide; {flickers without 'Application.ShowMainForm:=false'}
  43.    Unit2.Test_Clipboard; {saves a graphic file into the clipboard}
  44.    Close;
  45.    end;
  46.  
  47. end.

Code: Pascal  [Select][+][-]
  1. unit Unit2; {this is a short extract from a COMMON Unit to display a Graphic
  2.              file and more and to save it into the Clipboard}
  3.  
  4. {$mode objfpc}{$H+}
  5.  
  6. interface
  7.  
  8. uses
  9.  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ClipBrd;
  10.  
  11. procedure Test_Clipboard;
  12.  
  13. implementation
  14.  
  15. type
  16.  
  17.  { TForm2 }
  18.  
  19.  TForm2 = class(TForm)
  20.   Button1: TButton;
  21.   procedure Button1Click(Sender: TObject);
  22.  private
  23.  public
  24.  end;
  25.  
  26. { TForm2 }
  27.  
  28. procedure TForm2.Button1Click(Sender: TObject);
  29.    {loads a Graphic file and saves it into the Clipboard}
  30.    var Picture1: TPicture;
  31.    begin
  32.    Picture1:=TPicture.Create;
  33.    Picture1.LoadFromFile('demo.bmp');
  34.    Clipboard.Assign(Picture1);
  35.    Picture1.Free;
  36.    end;
  37.  
  38. procedure Test_Clipboard;
  39.    {abbreviation for to call 'Form2' from within 1 procedure}
  40.    var Form2: TForm2;
  41.    begin
  42.    Form2:=TForm2.CreateNew(Application);
  43.  
  44.    Form2.Button1:=TButton.Create(Form2);
  45.    Form2.Button1.Parent:=Form2;
  46.    Form2.Button1.Caption:='Clipboard';
  47.    Form2.Button1.OnClick:=@Form2.Button1Click;
  48.  
  49.    Form2.ShowModal;
  50.    Form2.Free;
  51.    end;
  52.  
  53. end.

Versions:
 - Windows 7 with Lazarus 3.6.0 and 2.0.10
 - Linux Ubuntu 22.04 with Lazarus 3.4.0 and 2.0.10 GTK2

Thanks in advance.
« Last Edit: December 21, 2024, 04:40:50 pm by Hartmut »

jeremiah

  • New Member
  • *
  • Posts: 18
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #1 on: December 12, 2024, 02:32:28 am »
I grabbed your code and in Debian 12 Cinnamon it errors as you state. Then in Debian 12 KDE it works as you want. So perhaps the object is not initialized yet for the GTK?? Lord willing I can have a go later tonight and try.  :)

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #2 on: December 12, 2024, 04:51:01 pm »
Thank you jeremiah for testing. It would be great if you could find out, how feature

Code: Pascal  [Select][+][-]
  1. Application.ShowMainForm:=false;

has to be handled correctly.

wp

  • Hero Member
  • *****
  • Posts: 12523
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #3 on: December 12, 2024, 05:23:40 pm »
I still don't get the intention behind this... (A project without Application.Run is VERY unusual...). Do you want to provide a general-purpose form to display an image and copy it to the clipboard? In this case you do not need a project at all, just put the form into a package and add the package to all projects requiring this form.

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #4 on: December 12, 2024, 06:11:21 pm »
Thank you wp for asking. Obviously I have not explained my intention clear enough.

Everythings said above leads to the situation, that I want to start a program without showing the Main Form (but I want to have a Main Form, because for me this is the easiest way to handle an INI-file).

To hide the Main Form at program start I found in this Forum this command:

Code: Pascal  [Select][+][-]
  1. Application.ShowMainForm:=false;

I found no real documentation for this command and after a couple of experiments I had this code:

Code: Pascal  [Select][+][-]
  1.  ...
  2.  RequireDerivedFormResource:=True;
  3.  Application.Scaled:=True;
  4.  Application.Initialize;
  5.  Application.ShowMainForm:=false; {used to hide the main form}
  6.  Application.CreateForm(TForm1, Form1); {calls FormCreate()}
  7.  Form1.FormActivate(nil); {is not longer called automatically}
  8.  end.

This code worked perfectly for months on Windows and Linux. But now I added a new feature to save something into the clipboard and this does not work on Linux (but on Windows). I found out, that the reason for that clipboard problem in Linux is the kind of how I started my program, because if I do it the "normal way", the clipboard works on Linux too.

So my question is: How has the feature 'Application.ShowMainForm:=false' to be handled correctly? If I use it, how can I then pass control to my code (which is now in procedure 'TForm1.FormActivate' in Unit 'Unit1' of my first post)?

If there are more questions, please ask again. Thanks for your help.

jeremiah

  • New Member
  • *
  • Posts: 18
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #5 on: December 13, 2024, 12:58:26 am »
I think I know what you are wanting, non visible main form but then work from other forms in the project. I needed this years ago on Windows using Delphi and what seems like something trivial was not lol. I have that code stashed here somewhere and will try to find it.

This attached works here with Debian 12, Cinnamon and using Lazarus 4.0 RC1 and below down to 3.40. Hope it is in the right direction.

Also note that on Cinnamon (perhaps other GTK DTs too, no idea) when your application closes the clipboard is wiped so if you want it to remain search the source in Lazarus for "gtk_clipboard_set_image" (I think this is correct) then make a routine to put it back. The GTK text unit is provided.

Peace...

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #6 on: December 13, 2024, 04:07:52 pm »
Thank you jeremiah for your suggestion. With your code the clipboard works on Linux too, but:

You changed the initialization of the project into:
Code: Pascal  [Select][+][-]
  1. begin
  2.  RequireDerivedFormResource:=True;
  3.  Application.Scaled:=True;
  4.  Application.Initialize;
  5.  Application.CreateForm(TForm1, Form1);
  6.  Application.ShowMainForm:=false;
  7.  Application.Run;
  8. end.

and placed the start and run of my code completely into TForm1.FormCreate().

This way Application.Run() is only executed at the moment, after the program had finished. In my eyes this makes no sense (or am I wrong?). I deleted this line completely and I saw no difference (That's the reason why I commented it out in my demo in my first post).

And this way command 'Application.ShowMainForm:=false' is also executed only at the moment, after the program had finished. In my eyes this makes no sense too. I deleted this line completely and I saw no difference. I don't believe, that this is the intended way to use feature 'Application.ShowMainForm:=false'.

Placing the start of my code into TForm1.FormCreate() has another disadvantage: I wrote in reply #4:
Quote
but I want to have a Main Form, because for me this is the easiest way to handle an INI-file
AFAIK the INI-file is loaded _after_ TForm1.FormCreate(). That's why I started my code out from TForm1.FormActivate(), where the INI-file has been loaded and it's values can be used. In TForm1.FormCreate() this is not possible.

My question is still: what is the correct (intended) way to use the feature 'Application.ShowMainForm:=false'?
I don't believe that using this feature forces, that e.g. the standard use of an INI-file via the Main Form is not longer possible.

jeremiah

  • New Member
  • *
  • Posts: 18
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #7 on: December 13, 2024, 10:57:53 pm »
If your having issues with the INI file that is probably because you omitted Application.Run in the project file (lpr). This initiates the message loop for the application. No reason to do this and if you do you will have to build your own loop, not recommended.

The Application.ShowMainForm := false; by rights, should be before the CreateForm but I have found over the years sometimes the order gets out of whack, I am speaking from Delphi experience here with Windows. So I learned years ago on Delphi to watch for changes in new versions and I'm stuck doing it still.

Application.ShowMainForm := false; does just that with the Visible property set to False.

If you are having issues with the application, something like sporadic things happening and you keep trying to work around them it is probably due to the omission of Application.Run, your message queue is not there.

It is something like this:

 repeat
    try
      HandleMessage;
    except
      HandleException(Self);
    end;
  until Terminated

So without Application.Run you do not have this and that is the number one culprit you are dealing with, at least as far as we know here.

The Form.OnCreate method is where to do your processing with the INI file. The form will not be shown until this procedure has completed. Or in your case not shown but the OnCreate procedure will be completed. You are not relegated to the main form here, you can initialize each form if needed when it is created. But you should get Application.Run back in your project. ;)

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #8 on: December 14, 2024, 01:41:23 pm »
Thank you jeremiah for your explanations. I appreciate that you are trying to help me and your knowledge. But in this case, from what I have tested, you are wrong in 2 points:

1) Calling Application.Run():
I added 4 writeln commands to your code in project1.lpr from reply #5 to have:
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.

and started your code. This way you can see, that Application.Run() is only executed at the moment, after the program had already finished. In my eyes this makes no sense. I totally agree with you, that Application.Run() *should* be called in a way, that it can do it's important job. But this way this is not possible. Please try it. Thanks.

2) Loading an INI-file:
I changed the project file of my real project to the code some lines above (that means I enabled Application.Run) and modified the end of procedure TForm1.FormCreate() to display some INI-variables. They were empty. (In procedure TForm1.FormActivate() they were filled). This test proves, that the INI-file is loaded after TForm1.FormCreate() has completed and can not be used inside. Please try it. Thanks.

To avoid misunderstandings:
I am not interested to find some workarounds / detours to handle an INI-file somehow nevertheless. I have multiple projects where I want to use feature 'Application.ShowMainForm:=false' but if this feature would mean (what I can't believe), that e.g. the standard use of an INI-file via the Main Form is not longer possible, than this would be the wrong feature for me. So please don't go in that direction.

I am convinced that we still don't use 'Application.ShowMainForm:=false' in the intended way, because then
 - Application.Run() is made useless
 - the standard use of an INI-file via the Main Form is not longer possible.

The intention of this Topic is to find someone who knows, what is the correct (intended) way to use feature 'Application.ShowMainForm:=false'? Thanks.

jeremiah

  • New Member
  • *
  • Posts: 18
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #9 on: December 14, 2024, 10:02:03 pm »
Anything after application.run is unreachable because the program is running in its message loop. If you were to write a simple windows dpr program and see how the loop works you would perhaps understand better.

Here is a snippet from a windows program here::

Code: Pascal  [Select][+][-]
  1.   //message loop
  2.   while GetMessage(Msg,0,0,0) do
  3.   begin
  4.    if (Msg.message = WM_KEYDOWN) and (Msg.wParam = VK_ESCAPE) then ReleaseResources;
  5.    if (Msg.message = WM_KEYDOWN) and (Msg.wParam = VK_F1) then menuAboutClick;
  6.    TranslateMessage(Msg);
  7.    DispatchMessage(Msg);
  8.   end;

As you can see in this snippet we are checking for keydown with the windows parameter and react accordingly. Nothing else is checked. This is where the program operates, in this loop until the user does something then we react. This is the old original way of coding using the windows api. This is where delphi changed everything when it was released by giving visual design and let us work on the meat of our code. Thus delphi/lazarus takes care of this message loop for us and we need not be concerned about it really. Without the application.run we loose this benefit and then need to do our own which is not something we want to do since in delphi the entire vcl is tied to this messaging, same in the lcl and a hat tip to all the folks in lazarus making this possible, thanks to all. :)

I think there is perhaps something else going on in your code, grabbing data from the ini file should be working.

I have a furnace giving up the ghost so if not back for a few days that is why. It is cold here and I need heat hahaha.

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #10 on: December 15, 2024, 11:57:57 am »
Hi jeremiah, there is absolutely no need to discuss about, that Application.Run() is important and should be used, because I totally agree with you.

Did you test your code from reply #5 with my small improvements from reply #8?
Apparently not, because then you would have seen, that Application.Run() this way is useless, because it is only started at the moment, after the program had already finished, where this loop makes no more sense.
Why didn't you test it?

440bx

  • Hero Member
  • *****
  • Posts: 4891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #11 on: December 15, 2024, 12:21:26 pm »
@Hartmut,

I know nothing about Linux but, you mentioned the following code works:
Code: Pascal  [Select][+][-]
  1. // with the following code block it works on Windows AND Linux:
  2.  
  3.  RequireDerivedFormResource:=True;
  4.  Application.Scaled:=True;
  5.  Application.Initialize;
  6.  Application.CreateForm(TForm1, Form1);
  7.  Application.Run;
  8.  
If I were you, instead of setting ShowMainForm to false, which seems to break that code, I'd try setting the form size to zero, which should make it invisible and if that doesn't  work, I'd try setting the form's x,y origin to be off screen so it is not visible.

Essentially, ways that make the form invisible without setting its visible property to false.

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #12 on: December 15, 2024, 12:53:40 pm »
If I were you, instead of setting ShowMainForm to false, which seems to break that code, I'd try setting the form size to zero, which should make it invisible and if that doesn't  work, I'd try setting the form's x,y origin to be off screen so it is not visible.

Thank you 440bx for your suggestion. I had already such a solution, before I found feature 'Application.ShowMainForm:=false'.
 - you can't really set the form size to zero, the form always will have a small minimal size
 - you can't set the forms origin to be off screen, it will always be moved into the visible area.

So this was my "solution":
Code: Pascal  [Select][+][-]
  1. self.SetBounds(Screen.DesktopWidth-1,Screen.DesktopHeight-1,1,1);

But this solution is a) not very elegant and b) flickers a little. I will stay with that, if nobody knows, how the correct (intended) use of feature 'Application.ShowMainForm:=false' is to be done.

440bx

  • Hero Member
  • *****
  • Posts: 4891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #13 on: December 15, 2024, 01:09:53 pm »
Thank you 440bx for your suggestion.
You're welcome.  I thought about those because I know they work under Windows (though not sure anymore about the 0,0,0,0) so I thought it would be worth a shot on Linux but you had already tried those.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Hartmut

  • Hero Member
  • *****
  • Posts: 891
Re: Starting a program with hiding the Main Form: what did I wrong?
« Reply #14 on: December 15, 2024, 01:58:14 pm »
I changed the subject of this Topic in the hope to find someone, who knows what is the correct (intended) use of feature 'Application.ShowMainForm:=false'. The way it was used by jeremiah and me has 2 disadvantages:
 - Application.Run() is only started at the moment, after the program had already finished, where this loop makes no more sense.
 - The standard use of an INI-file via the Main Form is not longer possible.

I am convinced that we use 'Application.ShowMainForm:=false' in a wrong way. What is the correct (intended) way, which does not have this 2 disadvantages?
Thanks a lot.

 

TinyPortal © 2005-2018