Recent

Author Topic: [SOLVED]I want to understand something regarding Loops and TTimer  (Read 1942 times)

Christian Becker

  • New member
  • *
  • Posts: 9
Hello,

for my little weather application I wanted to make it so that it updates after a given amount of time.
Since entering a city name and pressing a button to query the weather worked well, I thought I'd just add a loop to the OnButtonClick procedure with a sleep(5000) to get an update every 5 seconds.

I learned that this does not work and that instead a TTimer would (and it does, at least for a small clock program I wrote to test it).

So this works:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. begin
  3.   Label1.Caption:=TimeToStr(now);
  4. end;

while that doesn't:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   Repeat
  4.     Label1.Caption:=TimeToStr(now);
  5.     sleep(1000);
  6.   Until false;
  7. end;
  8.  

The possible problems with an infinite loop aside... why does it not work. I'd like to understand that.
And a bonus question:

This does not work as intended:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.     Label1.Caption:=TimeToStr(now);
  4.     sleep(1000);
  5.     Label1.Caption:=TimeToStr(now);
  6. end;
  7.  
I expected that procedure to show the time on the form and after one second update the time to (now)+1s. It does not. Moreover, the longer the sleep(), the longer it takes before the time is displayed.
Why is that?
I feel that if I understand that, I'll have understood something fundamental.

Thanks for reading.
« Last Edit: January 06, 2021, 04:55:26 pm by Christian Becker »

Warfley

  • Hero Member
  • *****
  • Posts: 1749
Re: I want to understand something regarding Loops and TTimer
« Reply #1 on: January 06, 2021, 04:42:20 pm »
This is because of the way how GUI applications work. A GUI application can be understood as an infinite loop with a message queue. When a button is clicked, os something like that, an event is fired. Events are then executed in order.
Changeing the GUI is also done via events. When you set the caption of the label, this sends a change caption event to that label. But the change will only be executed when all prior events finished.

You are sending that event from your FormCreate event. So, the changes can only take place if that FormCreate event finished. It finishes when your procedure is exited, i.e. after the last line of code in that function. This means, you send an event to change the label, then wait and send another event, but the previous event couldn't be executed. So once your function returns, both events get executed directly after another, meaning the change will be instant.

To solve this you can manually call the message queue and tell it to handle all waiting messages before continuing:
Code: Pascal  [Select][+][-]
  1.     Label1.Caption:=TimeToStr(now);
  2.     Application.ProcessMessages; // handle the message created by updating the caption
  3.     sleep(1000);
  4.     Label1.Caption:=TimeToStr(now);
But this also executes user messages like clicks on buttons, so be careful with that call.

You don't have the problem with the timer, because your each tick of your timer is exactly one event, so once the timer ticks, you change the label, the function returns and the lable changed event can be executed directly afterwards

Christian Becker

  • New member
  • *
  • Posts: 9
Re: I want to understand something regarding Loops and TTimer
« Reply #2 on: January 06, 2021, 04:55:07 pm »
Wow, thanks for the fast answer - which I also understood. :)

Handoko

  • Hero Member
  • *****
  • Posts: 5376
  • My goal: build my own game engine using Lazarus
Re: [SOLVED]I want to understand something regarding Loops and TTimer
« Reply #3 on: January 06, 2021, 05:06:57 pm »
Here I wrote a simple demo for you, so you can better understand how to use TTimer.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, StdCtrls, Spin, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Label1: TLabel;
  17.     Label2: TLabel;
  18.     Label3: TLabel;
  19.     SpinEdit1: TSpinEdit;
  20.     Timer1: TTimer;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure FormCreate(Sender: TObject);
  23.     procedure SpinEdit1Change(Sender: TObject);
  24.     procedure Timer1Timer(Sender: TObject);
  25.   private
  26.  
  27.   public
  28.  
  29.   end;
  30.  
  31. var
  32.   Form1: TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. { TForm1 }
  39.  
  40. procedure TForm1.FormCreate(Sender: TObject);
  41. begin
  42.   Timer1.Enabled  := False;
  43.   Label1.Caption  := '';
  44.   Button1.Caption := 'Start';
  45. end;
  46.  
  47. procedure TForm1.SpinEdit1Change(Sender: TObject);
  48. begin
  49.   Timer1.Interval := SpinEdit1.Value;
  50. end;
  51.  
  52. procedure TForm1.Timer1Timer(Sender: TObject);
  53. begin
  54.   Label1.Caption := TimeToStr(Now);
  55. end;
  56.  
  57. procedure TForm1.Button1Click(Sender: TObject);
  58. begin
  59.   case Button1.Caption of
  60.     'Start':
  61.       begin
  62.         Timer1.Interval := SpinEdit1.Value;
  63.         Timer1.Enabled  := True;
  64.         Button1.Caption := 'Stop';
  65.       end;
  66.     'Stop':
  67.       begin
  68.         Timer1.Enabled  := False;
  69.         Button1.Caption := 'Start';
  70.       end;
  71.   end;
  72. end;
  73.  
  74. end.

Christian Becker

  • New member
  • *
  • Posts: 9
Re: [SOLVED]I want to understand something regarding Loops and TTimer
« Reply #4 on: January 06, 2021, 06:41:23 pm »
Hey, many thanks.
I've already seen something very useful in that code.  :)
Very helpful!

 

TinyPortal © 2005-2018