What is the correct way to execute instructions AFTER opening a form lazarus / 31-LCL ---------------------------------------------------------------------- bruce.button · 2016-05-31 12:23 I am trying to execute a procedure AFTER opening a form (the procedure must play a sound, to be precise). If I use the OnActivate or OnShow events, the sound finishes playing (it takes a few seconds) BEFORE the form appears. It seems like I can get this to work using a timer, but I am wondering what the correct way of doing this is. Is there another event which will first display the form and then execute the code? Thanks in advance! ---------------------------------------------------------------------- Thaddy · 2016-05-31 12:35 I have this bizarre notion that overriding OnPaint would help But in that case use an assignable constant or a form variable to do it just once. Set a boolean. - Call inherited. - Beep No clue if it works ---------------------------------------------------------------------- molly · 2016-05-31 12:55 afaik there are several options: - timer as you already did - application.OnIdle event (don't forget to remove/block directly) - application.QueueAsyncCall - Sendmessage/Postmessage yourself a (window) message and respond to it. - Use a secondary form (or module) to play your sound. In case necessary waiting for the form to be active, visible or whatever you deem necessary Don't know if it is even possible to say that there is one single correct way. Thaddy's idea might work just as perfectly as well (i hadn't even considered that, so thanks for that) :-) I usually use the QueueAsyncCall myself, sometime using an additional Module that waits for the Main Form to become active. I do not have much experience with the other solutions mentioned. ---------------------------------------------------------------------- Thaddy · 2016-05-31 13:35 To my surprise it works perfectly (it remains a silly solution ) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$J+}  //Connect this to Form.OnPaint://assignable typed constants must be on. Hence $J+procedure TForm1.FormPaint(Sender: TObject);const B:Boolean = false;begin inherited; if not B then begin   b := true;   beep; end; end;  Must be a more logical solution, but if you are happy I am happy ---------------------------------------------------------------------- bruce.button · 2016-05-31 18:54 Thank you, Thaddy and Molly for the help. In my experiments the OnPaint method almost worked. The form opened, and then the sound played before the content of the form was displayed. Maybe I got something wrong (perhaps the {$J+} compiler directive, I'm not sure. In the end I found that the timer method was the easiest to implement. Unfortunately I don't understand enough to try the AsyncCall method. Thanks again, much appreciated! ---------------------------------------------------------------------- molly · 2016-05-31 19:11 You can never have too many options so there you go: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs; type   { TForm1 }   TForm1 = class(TForm)    procedure FormShow(Sender: TObject);  private    { private declarations }    procedure DoMyAsync(Data: PtrInt);  public    { public declarations }  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormShow(Sender: TObject);const B:Boolean = false;begin  // form is starting to show, call our delayed routine only once. Some code might have been borrowed from Thaddy here :-S  if not B then  begin    b := true;    Application.QueueAsyncCall(@DoMyASync, 10000);  end;end; procedure TForm1.DoMyAsync(Data: PtrInt);begin  // use the given parameter (1000 for this example) as a delay so that form  // has time to show itself completely. Change value according to your wishes  // or remove the sleep function altogether.  Sleep(Data);  // Make some noise  beep;end; end.   edit: corrected as per rvk's correction ---------------------------------------------------------------------- rvk · 2016-05-31 19:18 Quote from: molly on May 31, 2016, 09:11:00 pmYou can never have too many options so there you go: This doesn't work. It just delays the showing of the form the first time. (Make the delay 10 seconds and you see it) Quote from: molly on May 31, 2016, 02:55:41 pmafaik there are several options: - Sendmessage/Postmessage yourself a (window) message and respond to it. I thought this one should work but I just tested it and it also doesn't work. It IS the way to do this in Delphi but it just doesn't work in Lazarus because of the different message-handling. (It show a blank form and executes the procedure and paints the form after that) Edit: Ah, you meant to do this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Application.QueueAsyncCall(@DoMyASync, 10000);Yeah, that works ---------------------------------------------------------------------- molly · 2016-05-31 19:41 Quote from: rvk on May 31, 2016, 09:18:56 pmThis doesn't work. It just delays the showing of the form the first time. (Make the delay 10 seconds and you see it) You are entirely correct there rvk. Thanks for spotting my clumsiness.  QuoteEdit: Ah, you meant to do this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Application.QueueAsyncCall(@DoMyASync, 10000);Yeah, that works Oops. In my haste pasting i used the wrong call. Indeed that was the idea. I've corrected the original example. Thanks. QuoteQuote from: molly on May 31, 2016, 02:55:41 pmafaik there are several options: - Sendmessage/Postmessage yourself a (window) message and respond to it. I thought this one should work but I just tested it and it also doesn't work. It IS the way to do this in Delphi but it just doesn't work in Lazarus because of the different message-handling. (It show a blank form and executes the procedure and paints the form after that) Thank you for having tried and reported on that. I'll definitely give that a go as indeed i took my queue from Delphi with that suggestion. At least i'll not recommend it from now on until proofed working. Triple thank you is in order. ---------------------------------------------------------------------- rvk · 2016-05-31 20:05 Quote from: molly on May 31, 2016, 09:41:28 pm I've corrected the original example.I was also wondering about that local variable b you have in formshow. Doesn't that get reset to false each time formshow is called (because it's a local variable)? I could be wrong but my instinct would be to make it a global variable (or better yet part of the form so you can have multiple instances of that form). ---------------------------------------------------------------------- molly · 2016-05-31 20:13 QuoteI was also wondering about that local variable b you have in formshow. Doesn't that get reset to false each time formshow is called (because it's a local variable)? The behaviour as known to me is that a constant defined inside a local procedure keeps it value. But yes, if being strict then it should be a global or at least a form variable. Please consider the following: Code: [Select]program constantinopel; procedure test; Const x: integer = 0; begin   inc(x);   writeln(x);  end; var   i: integer; begin   for i := 0 to 10 do Test; end. Which outputs for me: Quote1 2 3 4 5 6 7 8 9 10 11 ---------------------------------------------------------------------- RAW · 2016-05-31 20:14 Did I miss something ?? Why not like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Unit uSoundAfterCreate; {$mode objfpc}{$H+} Interface Uses  Classes, Windows, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;  Type  TForm1 = Class(TForm)   Image1: TImage;     Procedure FormCreate    (Sender: TObject);    Procedure FormPaint     (Sender: TObject);    Procedure tiPlaySoundDo (Sender: TObject);  End;  VAR  Form1     : TForm1;  SoundReady: Boolean = False;  Ti        : TTimer;Implementation {$R *.lfm}  Procedure TForm1.FormCreate(Sender: TObject); Begin  Ti         := TTimer.Create(Nil);  Ti.Interval:= 30;  Ti.Enabled := False;  Ti.OnTimer := @tiPlaySoundDo;   Ti.Enabled:= True; End;  Procedure TForm1.FormPaint(Sender: TObject); Begin  //Form1.Update;  SoundReady:= True; End;  Procedure TForm1.tiPlaySoundDo(Sender: TObject); Begin  If SoundReady  Then   Begin    Ti.Enabled:= False;    Sleep(1000);    Windows.Beep(800, 800);   End; End;  End. ---------------------------------------------------------------------- rvk · 2016-05-31 20:25 Quote from: molly on May 31, 2016, 10:13:18 pmThe behaviour as known to me is that a constant defined inside a local procedure keeps it value.Ok, I wasn't sure about that. Now the only thing to test is what happens in another instance of TForm1 but I think it will be ok because it will inhabit a different memory-space. Quote from: SoE on May 31, 2016, 10:14:46 pmWhy not like this: You have an additional TTimer in memory which you don't use anymore. But if you are going to use a timer, I wouldn't use a global one because if you create multiple instances of your TForm1 you would get into trouble. In that case it's better to define the timer (and SoundReady) inside the form. ---------------------------------------------------------------------- RAW · 2016-05-31 20:40 QuoteYou have an additional TTimer in memory which you don't use anymore. But if you are going to use a timer, I wouldn't use a global one because if you create multiple instances of your TForm1 you would get into trouble. In that case it's better to define the timer (and SoundReady) inside the form. Do you mean like this... ?? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Unit uSoundAfterCreate; {$mode objfpc}{$H+} Interface Uses  Classes, Windows, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;  Type  TForm1 = Class(TForm)   Image1: TImage;     Procedure FormCreate    (Sender: TObject);    Procedure FormPaint     (Sender: TObject);    Procedure tiPlaySoundDo (Sender: TObject);      PRIVATE    SoundReady: Boolean;  End;  VAR  Form1     : TForm1;Implementation {$R *.lfm}  Procedure TForm1.FormCreate(Sender: TObject);  Var   Ti: TTimer; Begin  Ti         := TTimer.Create(Nil);  Ti.Interval:= 30;  Ti.Enabled := False;  Ti.OnTimer := @tiPlaySoundDo;   SoundReady:= False;  Ti.Enabled:= True; End;  Procedure TForm1.FormPaint(Sender: TObject); Begin  SoundReady:= True; End;  Procedure TForm1.tiPlaySoundDo(Sender: TObject); Begin  If SoundReady  Then   Begin    TTimer(Sender).Enabled:= False;    Sleep(400);    Windows.Beep(800, 800);     TTimer(Sender).Free;   End; End;  End. ---------------------------------------------------------------------- rvk · 2016-05-31 20:48 Quote from: SoE on May 31, 2016, 10:40:43 pmDo you mean like this... ?? I'm not sure if you are allowed to free the timer in it's own event handler. Doesn't this raise an exception? You could also put the ttimer in the private section of the form so you don't have to use ttimer(sender). But if you're not allowed to free the timer it would stay in memory. But at least now it's local to the form so if you have multiple tform1s they don't get in the way of each other. ---------------------------------------------------------------------- RAW · 2016-05-31 20:55 @rvk Thanks for the hint. I didn't get any error and Lazarus shows " 0 unfreed memory blocks: 0"... so it looks good so far... ---------------------------------------------------------------------- marcov · 2016-05-31 21:05 Such tricks (postmessage, queueasynccall etc) only work fine when no other components use something similar and trigger multiple times too. Also IIRC very heavy scaling of forms might trigger a multitude of events. Simplest is to set a timer for 100ms or so. ---------------------------------------------------------------------- rvk · 2016-05-31 21:09 Quote from: SoE on May 31, 2016, 10:55:19 pmI didn't get any error and Lazarus shows " 0 unfreed memory blocks: 0"... so it looks good so far...It might work in this simple example but I can guarantee you, you'll get into trouble at some point. You're freeing the ttimer after which the code returns to an already freed object (because you freed the ttimer in it's own event handler). Because in this small time- window the memory space isn't used, the code will run fine but that not always guaranteed. The rule is... Never ever free an object in it's own event handlers. ---------------------------------------------------------------------- RAW · 2016-05-31 21:43 QuoteNever ever free an object in it's own event handlers. OK, got that... But then I don't see where I should free the timer... maybe that was the point I didn't see... ---------------------------------------------------------------------- rvk · 2016-05-31 21:49 The only (good) way to free the timer is outside it's evenr handler. You could do that with a postmessage (to the form itself and destroy the timer in there) or the before mentioned QueueAsyncCall. But that kind of defeats the purpose of the ttimer because you could have used that QueueAsyncCall to begin with The only other option I see is to just leave the ttimer in memory and let it get destroyed by the form. If you do ttimer.create(self) (self is the form) it will be freed with the form. You could also free it in the form.destroy. ---------------------------------------------------------------------- howardpc · 2016-05-31 21:57 Quote from: molly on May 31, 2016, 10:13:18 pmThe behaviour as known to me is that a constant defined inside a local procedure keeps it value. That is only true for typed constants, and only true in the {$J+} compiler state (as Thaddy pointed out), which is normally true, since {$J+} is the default FPC setting, for historical reasons of Delphi compatibility. With {$J+} typed constants are assignable wherever they are declared (whether in a local procedure or not). As has been often noted, the word constant in "typed constant" is rather misleading in this case, since the 'constant' is actually an initialized variable. Of course it does remain constant ... until you change its value. ---------------------------------------------------------------------- RAW · 2016-05-31 22:13 @rvk Thanks, that is working fine so far... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Unit uSoundAfterCreate; {$mode objfpc}{$H+} Interface Uses  Classes, Windows, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;  Type  TForm1 = Class(TForm)   Image1: TImage;     Procedure FormCreate    (Sender: TObject);    Procedure FormPaint     (Sender: TObject);    Procedure tiPlaySoundDo (Sender: TObject);      PRIVATE    SoundReady: Boolean;      Ti        : TTimer;       Procedure WndProc(Var MSG: TMessage); Override;  End;  CONST  FreeTimerMsg = WM_USER + 555;  VAR  Form1     : TForm1;Implementation {$R *.lfm}  Procedure TForm1.FormCreate(Sender: TObject); Begin  Ti       := TTimer.Create(Nil);  Ti.Interval:= 30;  Ti.Enabled := False;  Ti.OnTimer := @tiPlaySoundDo;   SoundReady:= False;  Ti.Enabled:= True; End;  Procedure TForm1.FormPaint(Sender: TObject); Begin  SoundReady:= True; End;  Procedure TForm1.tiPlaySoundDo(Sender: TObject); Begin  If SoundReady  Then   Begin    Ti.Enabled:= False;  Sleep(400);    Windows.Beep(800, 800);     PostMessage(Form1.Handle, FreeTimerMsg, 0,0);   End; End;  Procedure TForm1.WndProc(Var MSG: TMessage); Begin  If MSG.Msg = FreeTimerMsg  Then Ti.Free  Else Inherited WndProc(MSG); End;  End. ---------------------------------------------------------------------- molly · 2016-06-01 20:41 Quote from: howardpc on May 31, 2016, 11:57:25 pmQuote from: molly on May 31, 2016, 10:13:18 pmThe behaviour as known to me is that a constant defined inside a local procedure keeps it value. That is only true for typed constants, and only true in the {$J+} compiler state (as Thaddy pointed out), which is normally true, since {$J+} is the default FPC setting, for historical reasons of Delphi compatibility. With {$J+} typed constants are assignable wherever they are declared (whether in a local procedure or not). As has been often noted, the word constant in "typed constant" is rather misleading in this case, since the 'constant' is actually an initialized variable. Of course it does remain constant ... until you change its value. Uhm... pardon me in advance, but i believe i misunderstood the first part of what you just wrote as it doesn't seem to add up for me (most probably wrong interpretation on my part). In mode J- you are not allowed to change the value of a constant. So, we can rule out that it ever changes, and therefor will always contain the last assigned value (it keeps it value). In mode J+ you are allowed to change the value of a constant but only when type defined. If it is not type defined the compiler does not allow you to change it during runtime and in case it is type defined it will keep its value (that was last assigned to it). I admit i could perhaps have been more explicit in my wording: The behaviour as known to me is that a constant will always contain the value that was last assigned to it. With regards to the second part of your message i agree that the name constant is misleading but on the other hand such constants behave differently as variables do when the circumstances are similar (e.g. variables defined in same way 'initializes' itself to again on re- entry of the procedure/function). ---------------------------------------------------------------------- howardpc · 2016-06-01 21:10 Quote from: molly on June 01, 2016, 10:41:20 pmIn mode J- you are not allowed to change the value of a constant. So, we can rule out that it ever changes, and therefor will always contain the last assigned value (it keeps it value). In mode J+ you are allowed to change the value of a constant but only when type defined. If it is not type defined the compiler does not allow you to change it during runtime and in case it is type defined it will keep its value (that was last assigned to it). You summarize  the possibilities well. We were both rather imprecise in what we wrote earlier. ---------------------------------------------------------------------- bruce.button · 2016-06-04 11:22 A big thank you to everyone who posted on this thread. I've learned a lot! ---------------------------------------------------------------------- RAW · 2017-09-26 15:30 This works for me very fine.... Without "Application.QueueAsyncCall(@AsyncSound, 0);" the Beep-sound plays but the window is not fully visible... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i UNIT Unit1;{$MODE OBJFPC}{$H+}{$J-} Interface USES  Windows, Classes,  SysUtils,  Forms,   Controls, StdCtrls;  TYPE  TForm1 = Class(TForm)    Button1: TButton;   Procedure Button1Click (Sender: TObject);     PRIVATE     Procedure AsyncSound(Data: PtrInt);  End;  VAR  Form1: TForm1; Implementation {$R *.LFM}  USES Unit2; Procedure TForm1.Button1Click(Sender: TObject); Begin  Form2:= TForm2.Create(Self);  Form2.Show;   // After FormCreation do whatever you like !!!  Form2.StaticText1.Caption:= 'I am Form2...';  Form2.Button1.Caption    := 'I am the first button';  Form2.Edit1.Text         := 'Hello Hello Hello';   // Windows.Beep(800, 1000);   Application.QueueAsyncCall(@AsyncSound, 0); End;  Procedure TForm1.AsyncSound(Data: PtrInt); Begin  Windows.Beep(800, 1000); End; END. It's probably a good idea to use a second thread or a sound lib to play longer sounds. Then the user can use the form while playing the sound... ---------------------------------------------------------------------- jamie · 2017-09-26 21:50 Widows has a PlaySound with Async option, what that means is the call will return to allow the app to continue on.