Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2014-03

TTimer woes (newbie)

8 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Michael Schnell
to Bob Axtell
Bob Axtell wrote
can someone show a newbie how to use the two system timers?

What do you mean by system timers ?

In a normal GUI based application you normally use TTimer for time-based actions.

You easily can create as many TTimers as you like (as well "visually" using the Lazarus GUI designer) as "dynamically" by having your program do myTimerN := TTimer.Create(Self); )

-Michael

Reply by email

Howard Page-Clark
to Michael Schnell
Michael Schnell wrote
On 03/31/2014 01:44 AM, Bob Axtell wrote: > can someone show a newbie how to use the two system timers? What do you mean by system timers ?

I think he means the TTimer and TIdleTimer found on the System page of the Component Palette.
If you drop one of each on the main form of a new project with a label and a memo, and add this code, it should give you an idea of how to use them.

-- code begin --

unit Unit1;
{$mode objfpc}{$H+}
interface
uses
   SysUtils, Forms, ExtCtrls, StdCtrls;
type
   { TForm1 }
   TForm1 = class(TForm)
     IdleTimer1: TIdleTimer;
     LAverageSoFar: TLabel;
     MNumbers: TMemo;
     Timer1: TTimer;
     procedure FormCreate(Sender: TObject);
     procedure IdleTimer1Timer(Sender: TObject);
     procedure Timer1Timer(Sender: TObject);
   private
     FNumber: integer;
     FRunningTotal: integer;
     FCount: integer;
   end;
var
   Form1: TForm1;
implementation
{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
   Randomize;
   MNumbers.Clear;
   Timer1.Interval:=500;
   ;
   Timer1.Enabled:=True;
   IdleTimer1.Interval:=1000;
   ;
   IdleTimer1.Enabled:=True;
end;
procedure TForm1.IdleTimer1Timer(Sender: TObject);
begin
   LAverageSoFar.Caption:=
     Format('Average of random numbers so far is
%n',[FRunningTotal/FCount]);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
   FNumber:=Random(101);
   MNumbers.Lines.Add(IntToStr(FNumber));
   Inc(FCount);
   Inc(FRunningTotal, FNumber);
end;
end.

-- code end --

Howard

Reply by email

Bob Axtell
to Howard Page-Clark

mine (a morse-code blinker) just doesn't work:

unit Unit1;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;
type
  { TForm1 }
  TForm1 = class(TForm)
    Panel1: TPanel;
    Timer1: TTimer;
    procedure FormKeyPress(Sender: TObject; var Key: char);
    procedure Timer1Timer(Sender: TObject);
  private
    { private declarations }
    procedure morse;
    procedure dash;
    procedure dot;
    procedure wait(x: longint);
    { public declarations }
 end;
var
  Form1: TForm1;
  a,b,i: byte;
  count,dat,
  ms,ls,mchar: byte;
  tick: integer;
  col: integer = clblue;
  mtbl: string[16] =
   #$bf#$be#$bc#$b8#$b0#$a0#$a1#$a3#$a7#$af#$42#$81#$85#$61#$20#$84;
implementation
{$R *.lfm}

{ TForm1 }

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 timer1.enabled:= false;
end;
procedure TForm1.wait(x: longint);
begin
 tick:= x;
 while tick > 0 do
  begin
    timer1.enabled:= true;
    repeat until not timer1.enabled;
    dec(tick)
  end;
end;
procedure Tform1.dash;
begin
 Panel1.color:= col;
 wait(30);
 Panel1.color:= clwhite;
 wait(10);
end;
procedure Tform1.dot;
begin
 panel1.color:= col;
 wait(10);
 panel1.color:= clwhite;
 wait(10);
end;
procedure tform1.morse;
begin
 mchar:= $33;
 ms:= ord(mtbl[succ((mchar shr 4) and 15)]);
 ls:= ord(mtbl[succ(mchar and 15)]);
 count:= (ms shr 5) and 7;
 dat:= ms and 31;
 for i:= 1 to count do
 begin
  if ((dat and 1) > 0) then dash else dot;
  wait(10);
  dat:= dat shr 1;
 end;
 wait(50);
 { ms done, ls begins}
 count:= (ls shr 5) and 7;
 dat:= ls and 31;
 for i:= 1 to count do
 begin
  if ((dat and 1) > 0) then dash else dot;
  wait(10);
 end;
 wait(100);
end;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
   case key of
   'r': begin
         col:= clred;
         wait(15);
        end;
   'g': begin
         col:= clgreen;
         wait(15);
        end;
   'b': begin
         col:= clblue;
         wait(15);
        end;
   ' ': morse;
  end;
end;
end.

any ideas? using v1.0.14 under WinXP.

--Bob A

Howard Page-Clark wrote 96 lines
On 31/03/2014 10:25, Michael Schnell wrote: > On 03/31/2014 01:44 AM, Bob Axtell wrote: > >> can someone show a newbie how to use the two system timers? >> > > What do you mean by system timers ? > I think he means the TTimer and TIdleTimer found on the System page of the Component Palette. If you drop one of each on the main form of a new project with a label and a memo, and add this code, it should give you an idea of how to use them. -- code begin -- unit Unit1; {$mode objfpc}{$H+} interface uses SysUtils, Forms, ExtCtrls, StdCtrls; type { TForm1 } TForm1 = class(TForm) IdleTimer1: TIdleTimer; LAverageSoFar: TLabel; MNumbers: TMemo; Timer1: TTimer; procedure FormCreate(Sender: TObject); procedure IdleTimer1Timer(Sender: TObject); procedure Timer1Timer(Sender: TObject); private FNumber: integer; FRunningTotal: integer; FCount: integer; end; var Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin Randomize; MNumbers.Clear; Timer1.Interval:=500; ; Timer1.Enabled:=True; IdleTimer1.Interval:=1000; ; IdleTimer1.Enabled:=True; end; procedure TForm1.IdleTimer1Timer(Sender: TObject); begin LAverageSoFar.Caption:= Format('Average of random numbers so far is %n',[FRunningTotal/FCount]); end; procedure TForm1.Timer1Timer(Sender: TObject); begin FNumber:=Random(101); MNumbers.Lines.Add(IntToStr(FNumber)); Inc(FCount); Inc(FRunningTotal, FNumber); end; end. -- code end -- Howard -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20140331/56e4d92e/attachment.html>

Reply by email

Flávio Etrusco
to Bob Axtell
Bob Axtell wrote 130 lines
mine (a morse-code blinker) just doesn't work: unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls; type { TForm1 } TForm1 = class(TForm) Panel1: TPanel; Timer1: TTimer; procedure FormKeyPress(Sender: TObject; var Key: char); procedure Timer1Timer(Sender: TObject); private { private declarations } procedure morse; procedure dash; procedure dot; procedure wait(x: longint); { public declarations } end; var Form1: TForm1; a,b,i: byte; count,dat, ms,ls,mchar: byte; tick: integer; col: integer = clblue; mtbl: string[16] = #$bf#$be#$bc#$b8#$b0#$a0#$a1#$a3#$a7#$af#$42#$81#$85#$61#$20#$84; implementation {$R *.lfm} { TForm1 } procedure TForm1.Timer1Timer(Sender: TObject); begin timer1.enabled:= false; end; procedure TForm1.wait(x: longint); begin tick:= x; while tick > 0 do begin timer1.enabled:= true; repeat until not timer1.enabled; dec(tick) end; end; procedure Tform1.dash; begin Panel1.color:= col; wait(30); Panel1.color:= clwhite; wait(10); end; procedure Tform1.dot; begin panel1.color:= col; wait(10); panel1.color:= clwhite; wait(10); end; procedure tform1.morse; begin mchar:= $33; ms:= ord(mtbl[succ((mchar shr 4) and 15)]); ls:= ord(mtbl[succ(mchar and 15)]); count:= (ms shr 5) and 7; dat:= ms and 31; for i:= 1 to count do begin if ((dat and 1) > 0) then dash else dot; wait(10); dat:= dat shr 1; end; wait(50); { ms done, ls begins} count:= (ls shr 5) and 7; dat:= ls and 31; for i:= 1 to count do begin if ((dat and 1) > 0) then dash else dot; wait(10); end; wait(100); end; procedure TForm1.FormKeyPress(Sender: TObject; var Key: char); begin case key of 'r': begin col:= clred; wait(15); end; 'g': begin col:= clgreen; wait(15); end; 'b': begin col:= clblue; wait(15); end; ' ': morse; end; end; end. any ideas? using v1.0.14 under WinXP. --Bob A

TTimer in Windows (and the basic timer support in Windows ) is implemented using window messages and you're not allowing the app to process messages inside your loop.

I guess simply changing your loop to:
+++

   repeat
      Application.ProcessMessages;
   until not timer1.enabled;

+++
should make it work.
If it's no problem that your app freezes the UI during the wait (like it does right now) you could just use Sleep() and abandon TTimer. OTOH the usual way to use TTimer is to do processing in the OnTimer handler, but then you'd have to change your code to store some state.

Regards,
Fl?vio
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20140331/204894d3/attachment.html>

Reply by email

Michael Schnell
to Flávio Etrusco
Fl?vio Etrusco wrote
TTimer in Windows (and the basic timer support in Windows ) is implemented using window messages and you're not allowing the app to process messages inside your loop.

This necessity is not limited to Windows nor to TTimer.

Any Object Pascal Event handler (hence most code done in "normal" projects) is driven by the Project's Event Queue (which in Windows in turn is driven by Windows messages).

In fact using "Application.ProcessMessages" in a loop is a workaround that is necessary when you really need a long winding loop in one of the (main Thread-) - Events) calculating something.

It is a lot better style to do short events, whenever possible (maybe doing the long winding stuff in a TThread). Infinite (polling) loops in fact are (close to) forbidden, as they eat up CPU cycles that can better be used for other activities in the project or in any other program running on that PC.

In fact Bob is not doing this (eating up CPU Cycles), but he uses wait(), which only hampers it's own project. This also usually is (close to) forbidden in the main thread, as the GUI will stop working while the main thread waits.

Thus he should either use a TTimer instead of waiting and continue the work to be done in a newly entered Timer Event, or execute the code that includes the wait() in a TThread.

-Michael

Reply by email

Howard Page-Clark
to Michael Schnell
Michael Schnell wrote
In fact Bob is not doing this (eating up CPU Cycles), but he uses wait(), which only hampers it's own project.

Actually Bob's "wait" uses his own TTimer-based code, and is not well designed.
As Flavio wrote, he would be best to replace all his waits with a Sleep, and remove his timer altogether.

Howard

Reply by email

Paul Breneman
to Flávio Etrusco: TTimer in Windows (and the basic timer support in Windows ) is implemented using window…

On 03/31/2014 01:51 PM, Fl?vio Etrusco wrote:
...

Show quoted text 7 lines
I guess simply changing your loop to: +++ repeat Application.ProcessMessages; until not timer1.enabled; +++ should make it work.

That loop will work, but the CPU usage will be 100% and the CPU will run warm and the battery life on a laptop will be bad. I'd normally add a Sleep(10) in the loop to avoid those problems.

Best regards,
Paul
www.TurboControl.com

Reply by email