Recent

Author Topic: [SOLVED] problem closing 2 dynamically created forms  (Read 3619 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 751
[SOLVED] problem closing 2 dynamically created forms
« on: October 28, 2017, 11:23:37 am »
My program has a problem and I don't see my mistake: my program can open 2 dynamically created forms (to show some informations) with the following rules:
 - it is allowed that both forms are opened at the same time
 - it is not allowed that the same form is opened more than once
 - the forms should be opened non-modal
 - when the main program terminates, both forms must be closed automatically

Code: Pascal  [Select][+][-]
  1. // This program makeks no sense. It's only an extract of a bigger
  2. // program and reduced to the minimum to demonstrate the problem.
  3.  
  4. {$mode objfpc}{$H+}
  5. {$apptype console}
  6.  
  7. unit Unit1;
  8.  
  9. interface
  10.  
  11. uses SysUtils, Forms, StdCtrls; {reduced to the minimum}
  12.  
  13. type
  14.  TForm1 = class(TForm)
  15.   Button1: TButton;
  16.   Button2: TButton;
  17.   procedure Button1Click(Sender: TObject);
  18.   procedure Button2Click(Sender: TObject);
  19.   procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  20.  private { private declarations }
  21.  public  { public declarations }
  22.  end;
  23.  
  24. var Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. type                                 {class for a dynamically created form: }
  31.    TFormX = class(TForm) {procedure for every close of the Form: }
  32.       procedure CloseX(Sender: TObject; var CloseAction: TCloseAction);
  33.  
  34.    private { private declarations }
  35.       pStop: ^boolean; {control variable for to close the form}
  36.    public  { public declarations }
  37.    end;
  38.  
  39. procedure TFormX.CloseX(Sender: TObject; var CloseAction: TCloseAction);
  40.    {called by event "OnClose" of the dynamically created forms}
  41.    begin
  42.    writeln('CloseX');
  43.    pStop^:=true; {stopps the waiting loop in show_Form()}
  44.    end;
  45.  
  46. procedure show_Form(msg: string; x0: integer; var stop: boolean);
  47.    {creates a Form dynamically and waites for closing the Form}
  48.    var FormX: TFormX;
  49.    begin
  50.    FormX:=TFormX.CreateNew(nil);
  51.    FormX.pStop:=@stop; {control variable for to close the form}
  52.    FormX.OnClose:=@FormX.CloseX; {procedure for event "OnClose"}
  53.    FormX.Caption:=msg; {show something in the form...}
  54.  
  55.    FormX.SetBounds(x0,300,150,150);
  56.    FormX.Show; {show the new form}
  57.  
  58.    writeln('started waiting for close');
  59.    stop:=false;                          {wait for the closing of the form: }
  60.    repeat Application.ProcessMessages;
  61.           sleep(1); {avoid high CPU-load}
  62.    until  stop; {variable changed in CloseX()}
  63.    writeln('stopped waiting');
  64.  
  65.    FreeAndNil(FormX);
  66.    end; {show_Form}
  67.  
  68. var stop1,stop2: boolean;
  69.  
  70. procedure TForm1.Button1Click(Sender: TObject);
  71.    const activ1: boolean = false;
  72.    begin
  73.    if activ1 then writeln('still running #1') else
  74.       begin
  75.       activ1:=true;
  76.       show_Form('hello-1', 300, stop1);
  77.       writeln('finished #1');
  78.       activ1:=false;
  79.       end;
  80.    end;
  81.  
  82. procedure TForm1.Button2Click(Sender: TObject);
  83.    const activ2: boolean = false;
  84.    begin
  85.    if activ2 then writeln('still running #2') else
  86.       begin
  87.       activ2:=true;
  88.       show_Form('hello-2', 500, stop2);
  89.       writeln('finished #2');
  90.       activ2:=false;
  91.       end;
  92.    end;
  93.  
  94. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  95.    begin
  96.    writeln('Form1_Close');
  97.    stop1:=true; {stopps waiting loop in show_Form()}
  98.    stop2:=true; {stopps waiting loop in show_Form()}
  99.    end;
  100.  
  101. end.

When you do the following:
 - click button 1 (opens form 1)
 - close form 1
 - click button 2 (opens form 2)
 - close form 2
 - click button 1 (opens form 1 again)
everything is fine. The output shows:
    started waiting for close
    CloseX
    stopped waiting
    finished #1
    started waiting for close
    CloseX
    stopped waiting
    finished #2
    started waiting for close

But when you do the following:
 - click button 1 (opens form 1)
 - click button 2 (opens form 2)
 - close form 1
 - click button 1 (should open form 1 again)
nothing happens. The output shows:
    started waiting for close
    started waiting for close
    CloseX
    still running #1

That means, closing of form #1 did not work correctly. After "CloseX" we should see "stopped waiting" and "finished #1", but this is not excecuted. Why?

To control the closing of the 2 forms, 2 global variables "stop1" and "stop2" are used. They are submitted to each form as a parameter and they are set to True in FormClose() when the main program terminates. The benefit is: FormClose() must not know 'TFormX' and it works independent of the dynamically forms having ever been created or not.

I'm using Lazarus 1.8.0 RC5 with FPC 3.0.4 but had the same results with Lazarus 1.6.2 and FPC 3.0.0. Both on Windows 7 32 bit. I attached a small project. Thanks a lot in advance.
« Last Edit: October 28, 2017, 02:54:04 pm by Hartmut »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: problem closing 2 dynamically created forms
« Reply #1 on: October 28, 2017, 11:50:27 am »
One main error, You call application.processmessages. Remove it.
1) The onclose event of a form has a parameter named CloseAction set it to caFree to inform the framework that you want the form to be destroyed.
2) the owner of a component, passed in the constructor, is responsible for destroying it. Pass the application as the owner for your forms, that will destroy them automatically, when the application ends.
« Last Edit: October 28, 2017, 12:03:33 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Hartmut

  • Hero Member
  • *****
  • Posts: 751
Re: problem closing 2 dynamically created forms
« Reply #2 on: October 28, 2017, 12:50:24 pm »
Thank you taazz for your reply.

I removed Application.ProcessMessages from the loop, but then, when pressing button 1, form #1 is not drawn completely and the program hangs (see screenshot).  So I inserted ProcessMessages again to the loop.

I attached "CloseAction:=caFree;" to both TFormX.CloseX() and TForm1.FormClose(), but then, when closing form #1, I get "Project project1 has raised Exception class 'External: SIGSEGV' at address 40c93f" and then an assembler window opens.

You wrote "Pass the application as the owner for your forms". How do I do that? (I'm a beginner to the LCL, sorry).

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: problem closing 2 dynamically created forms
« Reply #3 on: October 28, 2017, 01:18:35 pm »
I mend to remove the loop after the show completely here is a quick demonstration of how this works.

Code: Pascal  [Select][+][-]
  1. // This program makeks no sense. It's only an extract of a bigger
  2. // program and reduced to the minimum to demonstrate the problem.
  3.  
  4. {$mode objfpc}{$H+}
  5. {$apptype console}
  6.  
  7. unit Unit1;
  8.  
  9. interface
  10.  
  11. uses SysUtils, Forms, StdCtrls; {reduced to the minimum}
  12.  
  13. type
  14.  TForm1 = class(TForm)
  15.   Button1: TButton;
  16.   Button2: TButton;
  17.   procedure Button1Click(Sender: TObject);
  18.   procedure Button2Click(Sender: TObject);
  19.   procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  20.  private { private declarations }
  21.  public  { public declarations }
  22.  end;
  23.  
  24. var Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. type                                 {class for a dynamically created form: }
  31.    TFormX = class(TForm) {procedure for every close of the Form: }
  32.       procedure CloseX(Sender: TObject; var CloseAction: TCloseAction);
  33.  
  34.    public  { public declarations }
  35.    end;
  36.  
  37. procedure TFormX.CloseX(Sender: TObject; var CloseAction: TCloseAction);
  38.    {called by event "OnClose" of the dynamically created forms}
  39.    begin
  40.    writeln('CloseX');
  41.    //pStop^:=true; {stopps the waiting loop in show_Form()}
  42.      CloseAction := caFree;
  43.    end;
  44.  
  45. procedure show_Form(msg: string; x0: integer; var stop: boolean);
  46.    {creates a Form dynamically and waites for closing the Form}
  47.    var FormX: TFormX;
  48.    begin
  49.    FormX:=TFormX.CreateNew(Application);
  50.    FormX.OnClose:=@FormX.CloseX; {procedure for event "OnClose"}
  51.    FormX.Caption:=msg; {show something in the form...}
  52.  
  53.    FormX.SetBounds(x0,300,150,150);
  54.    FormX.Show; {show the new form}
  55.  end; {show_Form}
  56.  
  57. var stop1,stop2: boolean;
  58.  
  59. procedure TForm1.Button1Click(Sender: TObject);
  60.    const activ1: boolean = false;
  61.    begin
  62.    if activ1 then writeln('still running #1') else
  63.       begin
  64.       activ1:=true;
  65.       show_Form('hello-1', 300, stop1);
  66.       writeln('finished #1');
  67.       activ1:=false;
  68.       end;
  69.    end;
  70.  
  71. procedure TForm1.Button2Click(Sender: TObject);
  72.    const activ2: boolean = false;
  73.    begin
  74.    if activ2 then writeln('still running #2') else
  75.       begin
  76.       activ2:=true;
  77.       show_Form('hello-2', 500, stop2);
  78.       writeln('finished #2');
  79.       activ2:=false;
  80.       end;
  81.    end;
  82.  
  83. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  84.    begin
  85.    writeln('Form1_Close');
  86.    end;
  87.  
  88. end.
  89.  
The framework will make sure that the dynamic forms will be destroyed on exit. There is the small issue with identifying if a form is already open which can be solved in various ways eg. make actv1, activ2 fields of the form and pass them to the TformX's object to set to false on exit or search the active forms list for a TFormX based form and see if its parameters are the same with the ones you are asked to show bringing it to focus if you found one.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Hartmut

  • Hero Member
  • *****
  • Posts: 751
Re: problem closing 2 dynamically created forms
« Reply #4 on: October 28, 2017, 02:01:21 pm »
The solution from taazz works fine. To solve the issue, that each form is not allowed to be opened more than once, I changed variables "activ1" and "activ2" to be global and to be reset in TFormX.CloseX(). See the final code below. Thanks a lot to taazz for your valuable help.

There is just one question left for me to learn: why does TFormX.CloseX() contain the recommended "CloseAction := caFree" and TForm1.FormClose() not?

Code: Pascal  [Select][+][-]
  1. // This program makeks no sense. It's only an extract of a bigger
  2. // program and reduced to the minimum to demonstrate the problem.
  3.  
  4. {$mode objfpc}{$H+}
  5. {$apptype console}
  6.  
  7. unit Unit1;
  8.  
  9. interface
  10.  
  11. uses SysUtils, Forms, StdCtrls; {reduced to the minimum}
  12.  
  13. type
  14.  TForm1 = class(TForm)
  15.   Button1: TButton;
  16.   Button2: TButton;
  17.   procedure Button1Click(Sender: TObject);
  18.   procedure Button2Click(Sender: TObject);
  19.   procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  20.  private { private declarations }
  21.  public  { public declarations }
  22.  end;
  23.  
  24. var Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. type                                 {class for a dynamically created form: }
  31.    TFormX = class(TForm) {procedure for every close of the Form: }
  32.       procedure CloseX(Sender: TObject; var CloseAction: TCloseAction);
  33.  
  34.    private { private declarations }
  35.       pActiv: ^boolean; {control variable to reset on closing the form}
  36.    public  { public declarations }
  37.    end;
  38.  
  39. procedure TFormX.CloseX(Sender: TObject; var CloseAction: TCloseAction);
  40.    {called by event "OnClose" of the dynamically created forms}
  41.    begin
  42.    writeln('CloseX');
  43.    pActiv^:=false; {allows the form to be opened again}
  44.    CloseAction:=caFree;
  45.    end;
  46.  
  47. procedure show_Form(msg: string; x0: integer; var activ: boolean);
  48.    {creates a Form dynamically}
  49.    var FormX: TFormX;
  50.    begin
  51.    FormX:=TFormX.CreateNew(Application);
  52.    FormX.pActiv:=@activ; {control variable to reset on closing the form}
  53.    FormX.OnClose:=@FormX.CloseX; {procedure for event "OnClose"}
  54.    FormX.Caption:=msg; {show something in the form...}
  55.  
  56.    FormX.SetBounds(x0,300,150,150);
  57.    FormX.Show; {show the new form}
  58.  end;
  59.  
  60. const activ1: boolean = false;
  61.       activ2: boolean = false;
  62.  
  63. procedure TForm1.Button1Click(Sender: TObject);
  64.    begin
  65.    if activ1 then writeln('still running #1') else
  66.       begin
  67.       activ1:=true;
  68.       show_Form('hello-1', 300, activ1);
  69.       end;
  70.    end;
  71.  
  72. procedure TForm1.Button2Click(Sender: TObject);
  73.    begin
  74.    if activ2 then writeln('still running #2') else
  75.       begin
  76.       activ2:=true;
  77.       show_Form('hello-2', 500, activ2);
  78.       end;
  79.    end;
  80.  
  81. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  82.    begin
  83.    writeln('Form1_Close');
  84.    end;
  85.  
  86. end.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: problem closing 2 dynamically created forms
« Reply #5 on: October 28, 2017, 02:10:45 pm »
The framework defaults to CloseAction = caHide for all forms unless the form is the application's main form in which case it defaults to caFree. Less problems with autocreated forms and expectation that it is still in memory after close.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: problem closing 2 dynamically created forms
« Reply #6 on: October 28, 2017, 02:21:28 pm »
Another solution would be to use an array of forms. Here is a working example that unhides/hides a form when the button is clicked. A second unit is used for the custom form and array definition. Instantiation of this second form is commented out in the lpr file.

Just an idea.

unit1:
Code: Pascal  [Select][+][-]
  1. unit uFormMain;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, uFormX;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Button2: TButton;
  17.     procedure Button1Click(Sender: TObject);
  18.     procedure Button2Click(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.   private
  21.     { private declarations }
  22.   public
  23.     { public declarations }
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.Button1Click(Sender: TObject);
  36. begin
  37.   if not FormX[0].Visible then
  38.     FormX[0].Show
  39.   else
  40.     FormX[0].Hide;
  41. end;
  42.  
  43. procedure TForm1.Button2Click(Sender: TObject);
  44. begin
  45.   if not FormX[1].Visible then
  46.     FormX[1].Show
  47.   else
  48.     FormX[1].Hide;
  49. end;
  50.  
  51. procedure TForm1.FormCreate(Sender: TObject);
  52. begin
  53.   SetLength(FormX, 1);
  54.   FormX[0] := TForm2.Create(Application);
  55.   FormX[1] := TForm2.Create(Application);
  56. end;
  57.  
  58. end.
  59.  
unit2:
Code: Pascal  [Select][+][-]
  1. unit uFormX;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
  9.  
  10. type
  11.  
  12.   { TForm2 }
  13.  
  14.   TForm2 = class(TForm)
  15.   private
  16.     { private declarations }
  17.   public
  18.     { public declarations }
  19.   end;
  20.  
  21. var
  22.   FormX: array of TForm2;
  23.  
  24. implementation
  25.  
  26. {$R *.lfm}
  27.  
  28. { TForm2 }
  29.  
  30. end.
  31.  

Note that both forms (hidden or shown) are destroyed when the application quits.
« Last Edit: October 28, 2017, 02:28:46 pm by Munair »
keep it simple

Hartmut

  • Hero Member
  • *****
  • Posts: 751
Re: problem closing 2 dynamically created forms
« Reply #7 on: October 28, 2017, 02:53:44 pm »
The solution from Munair is another interesting approach, because the forms are always created and only switched from Show to Hide.
Thanks a lot to taazz and Munair. This is a great forum!

mai

  • Full Member
  • ***
  • Posts: 133
  • truther
Re: [SOLVED] problem closing 2 dynamically created forms
« Reply #8 on: October 28, 2017, 03:19:59 pm »
better than bloody MX , anyway ...

 

TinyPortal © 2005-2018