Recent

Author Topic: TTimer in class does not trigger OnTimer event, why?  (Read 1469 times)

hamacker

  • Jr. Member
  • **
  • Posts: 59
TTimer in class does not trigger OnTimer event, why?
« on: September 05, 2024, 03:27:19 pm »
I have de code below, it's fail because the event ontimer does not trigger  when I started. I dont know why, I see everything OK. There is an expert present to appoint where I fail?
Code: Pascal  [Select][+][-]
  1. unit classe.blinklabel;
  2. (*
  3. Usage:
  4. var
  5.   BlinkLabel: TBlinkLabel;
  6. begin
  7.   BlinkLabel := TBlinkLabel.Create(Form1.Label1, 5); // Label1 will blink for 5 times
  8.   BlinkLabel.Start := True;
  9. *)
  10.  
  11. {$mode objfpc}{$H+}
  12.  
  13. interface
  14.  
  15. uses
  16.   Classes,
  17.   Forms,
  18.   SysUtils,
  19.   ExtCtrls,
  20.   StdCtrls,
  21.   Graphics;
  22.  
  23. type
  24.   TBlinkLabel = class(TObject)
  25.   private
  26.     FCountLimit: Integer;
  27.     FMyLabel: TLabel;
  28.     FStart: Boolean;
  29.     FMyTimer: TTimer;
  30.     FCurrentCount: Cardinal;
  31.     FOldFontStyles: TFontStyles;
  32.     FOldColor: TColor;
  33.     FOldMsg: String;
  34.     procedure SetStart(AValue: Boolean);
  35.     procedure OnTimer(Sender: TObject);
  36.   public
  37.     constructor Create(ALabel: TLabel; ACountLimit: Cardinal);
  38.     destructor Destroy; override;
  39.     property CountLimit: Integer read FCountLimit write FCountLimit;
  40.     property MyLabel: TLabel read FMyLabel write FMyLabel;
  41.     property Start: Boolean read FStart write SetStart;
  42.   end;
  43.  
  44. implementation
  45.  
  46. constructor TBlinkLabel.Create(ALabel: TLabel; ACountLimit: Cardinal);
  47. begin
  48.   inherited Create;
  49.   FMyLabel := ALabel;
  50.   FCountLimit := ACountLimit;
  51.   FCurrentCount := 0;
  52.   FOldFontStyles := ALabel.Font.Style;
  53.   FOldColor := ALabel.Color;
  54.   FOldMsg := ALabel.Caption;
  55.   // Creates and configures the Timer
  56.   FMyTimer := TTimer.Create(nil);
  57.   FMyTimer.Interval := 2000;  // 2-second interval
  58.   FMyTimer.OnTimer := @OnTimer;
  59.   FMyTimer.Enabled := False;
  60. end;
  61.  
  62. destructor TBlinkLabel.Destroy;
  63. begin
  64.   FMyTimer.Free;
  65.   inherited Destroy;
  66. end;
  67.  
  68. procedure TBlinkLabel.SetStart(AValue: Boolean);
  69. begin
  70.   if FStart = AValue then Exit;
  71.   FStart := AValue;
  72.  
  73.   if FStart then
  74.   begin
  75.     FCurrentCount := 0;
  76.     FMyTimer.Enabled := True;
  77.   end
  78.   else
  79.   begin
  80.     FMyTimer.Enabled := False;
  81.     // Returns to the initial state
  82.     FMyLabel.Font.Style := FOldFontStyles;
  83.     FMyLabel.Color := FOldColor;
  84.     FMyLabel.Caption := FOldMsg;
  85.     Self.Free;  // Class self-destruction
  86.   end;
  87. end;
  88.  
  89. procedure TBlinkLabel.OnTimer(Sender: TObject);
  90. begin
  91.   FMyTimer.Enabled := False;
  92.   Inc(FCurrentCount);
  93.  
  94.   // Toggles the font style between normal and bold
  95.   if FMyLabel.Caption = '' then
  96.     FMyLabel.Caption := FOldMsg
  97.   else
  98.     FMyLabel.Caption := '';
  99.   FMyLabel.Refresh;
  100.  
  101.   Beep;  // Debug purposes, if it reaches here, you will hear a beep
  102.   // Checks if the specified seconds have passed
  103.   if FCurrentCount >= FCountLimit then
  104.   begin
  105.     FMyLabel.Font.Style := FOldFontStyles;  // Restores the font to the initial state
  106.     FMyLabel.Color := FOldColor;
  107.     FMyLabel.Caption := FOldMsg;
  108.     Self.Free;  // Class self-destruction
  109.   end
  110.   else
  111.   begin
  112.     Application.ProcessMessages;
  113.     FMyTimer.Enabled := True;
  114.   end;
  115. end;
  116.  
  117. end.

iLya2IK

  • New Member
  • *
  • Posts: 45
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #1 on: September 05, 2024, 03:43:20 pm »
Hello. Try to change this line

Code: Pascal  [Select][+][-]
  1. FMyTimer := TTimer.Create(nil);
  2. // to
  3. FMyTimer := TTimer.Create(ALabel.Owner);
  4.  

Thaddy

  • Hero Member
  • *****
  • Posts: 16419
  • Censorship about opinions does not belong here.
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #2 on: September 05, 2024, 03:44:56 pm »
start with unit classe.blinklabel; that is delphi code, not fpc.(yet)
The previous answer can also help you, but just if you are compiling for windows, which you do not mention.
« Last Edit: September 05, 2024, 03:48:13 pm by Thaddy »
There is nothing wrong with being blunt. At a minimum it is also honest.

hamacker

  • Jr. Member
  • **
  • Posts: 59
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #3 on: September 05, 2024, 03:56:26 pm »
I try Aplication.MainForm too, but still the same.
It's a Windows Application. Lazarus 3.4.

Hello. Try to change this line

Code: Pascal  [Select][+][-]
  1. FMyTimer := TTimer.Create(nil);
  2. // to
  3. FMyTimer := TTimer.Create(ALabel.Owner);
  4.  

alpine

  • Hero Member
  • *****
  • Posts: 1323
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #4 on: September 05, 2024, 04:25:57 pm »
Specifying the Owner won't help much. Most probably you're blocking the message processing somewhere. I see inappropriate Application.ProcessMessages at line 112. Not sure it is a problem, though.
Can you give a complete minimal project which shows the issue?
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

hamacker

  • Jr. Member
  • **
  • Posts: 59
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #5 on: September 05, 2024, 04:52:15 pm »
My intention is to have a status bar with a label inside and when convenient, this label simply blinks, but it cannot be blocking. As long as it is a class it seems that it does not work. Of course if I put a TTimer inside a form it will work normally. I think I will give up on this approach. I am now working on a way to make a label appear at a certain coordinate (like a hint) that disappears automatically. What do you think of this:

procedure CreateTimedLabel(AParent: TWinControl; const ACaption: TCaption; ATimeout: Cardinal);
  ATimeout: Cardinal);
var
  I: Integer;
  VLabel: TLabel;
begin
  VLabel := TLabel.Create(AParent);
  try
    VLabel.Parent := AParent;
    VLabel.Caption := ACaption;
    VLabel.Align := alBottom;
    I := GetTickCount64;
    repeat
      Application.ProcessMessages;
      if Application.Terminated then
        Break;
    until (GetTickCount64 - I) >= ATimeout;
  finally
    VLabel.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  CreateTimedLabel(Self, 'My Test', 10 * 1000);  //10 secs
end;

WooBean

  • Sr. Member
  • ****
  • Posts: 279
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #6 on: September 05, 2024, 04:53:18 pm »
That code works in Windows.

A unit (TForm) with added Label1 component:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   classe.blinklabel;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Label1: TLabel;
  17.     procedure Label1Click(Sender: TObject);
  18.   private
  19.  
  20.   public
  21.   var BlinkLabel: TBlinkLabel;
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.Label1Click(Sender: TObject);
  34. begin
  35.   if BlinkLabel=nil then BlinkLabel:=TBlinkLabel.Create(Form1.Label1, 5);
  36.   BlinkLabel.Start := True;
  37. end;
  38.  
  39. end.
  40.  

To start expected action needed clicking a Label1 caption. Not empty required.
Platforms: Win7/64, Linux Mint Ulyssa/64

alpine

  • Hero Member
  • *****
  • Posts: 1323
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #7 on: September 05, 2024, 05:03:50 pm »
I've just also tested the original code and I can confirm it works.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   BlinkLabel: TBlinkLabel;
  4. begin
  5.   BlinkLabel := TBlinkLabel.Create(Form1.Label1, 5); // Label1 will blink for 5 times
  6.   BlinkLabel.Start := True;
  7. end;

Lazarus 2.2.2 (rev lazarus_2_2_2) FPC 3.2.2 i386-win32-win32/win64

Edit: You can delete line #112.
« Last Edit: September 05, 2024, 05:07:01 pm by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Thaddy

  • Hero Member
  • *****
  • Posts: 16419
  • Censorship about opinions does not belong here.
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #8 on: September 05, 2024, 05:25:52 pm »
That is because it has a parent that listens to messages. Nil listens to nothing, nada, zilch.
That is not that it can not be done, but as implemented a TTimer needs a parent.
If the required platform is a unix derivative I have some code laying around, but not for windows.
( comes with a recent bug report)
« Last Edit: September 05, 2024, 05:29:55 pm by Thaddy »
There is nothing wrong with being blunt. At a minimum it is also honest.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10703
  • Debugger - SynEdit - and more
    • wiki
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #9 on: September 05, 2024, 06:44:31 pm »
About Owner/Parent:

I run the following on Win10 and Linux. (Based an a new GUI app)
Code: Pascal  [Select][+][-]
  1. procedure TForm1.DoFoo(Sender: TObject);
  2. begin
  3.   Caption := Caption + '.';
  4. end;
  5.  
  6. constructor TForm1.Create(TheOwner: TComponent);
  7. begin
  8.   // Create a Timer. At this point the main form has not even got a handle yet.
  9.   with TTimer.Create(nil) do begin
  10.     Interval := 2000;
  11.     OnTimer := @DoFoo;
  12.     Enabled := True;
  13.   end;
  14.  
  15.   inherited Create(TheOwner);
  16. end;
  17.  

And it works. Dots are appended to the caption.
It will leak mem. Without owner you must free the timer yourself, but that is not the point here.

So, a Timer can be created without owner.



I wonder why the original post has the "Application.ProcessMessages"?

- A timer will only trigger if the app does process it's messages. If the app is running some long time taking code, then the timer will only trigger if that code has handed control back.
- Since the timer makes visual changes, after the timer, there must be a paint event. If the app is busy with too many other messages (and longer run code in between) then the paint may be pushed back, and pushed back and ....

If you have other code that could block the app => use threads.

If your problem is the paint msg => Form.Repaint (afaik)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10703
  • Debugger - SynEdit - and more
    • wiki
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #10 on: September 05, 2024, 06:49:58 pm »
Off topic:
Quote
Code: Pascal  [Select][+][-]
  1. Beep;  // Debug purposes, if it reaches here, you will hear a beep

In the debugger, you can
- set a breakpoint
- open breakpoint properties and uncheck "break" (under "Actions")

Then open from menu: View > Debug Windows > breakpoints.
The breakpoint is listed in that window and has a "pass count". This count will be increased by one, each time the code passes the line.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1456
    • Lebeau Software
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #11 on: September 05, 2024, 09:29:02 pm »
So, a Timer can be created without owner.

Yes, because it creates its own hidden window for receiving timer messages.  An owner window is not required.  But an active message pump is.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

alpine

  • Hero Member
  • *****
  • Posts: 1323
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #12 on: September 06, 2024, 06:42:17 am »
Quote
So, a Timer can be created without owner.

In Window$ timers were created with a specified callback and thus they don't need a hWnd at all. Someone could have checked it out before expressing the way they did.
https://learn.microsoft.com/en-us/windows/win32/winmsg/using-timers
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

WooBean

  • Sr. Member
  • ****
  • Posts: 279
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #13 on: September 06, 2024, 07:27:50 am »
...
In Window$ timers were created with a specified callback and thus they don't need a hWnd at all. Someone could have checked it out before expressing the way they did.
https://learn.microsoft.com/en-us/windows/win32/winmsg/using-timers

Someone could have checked that TS code does use
Code: Pascal  [Select][+][-]
  1. FMyTimer := TTimer.Create(nil);
  2.  
before drawing conclusions too far.

Platforms: Win7/64, Linux Mint Ulyssa/64

Thaddy

  • Hero Member
  • *****
  • Posts: 16419
  • Censorship about opinions does not belong here.
Re: TTimer in class does not trigger OnTimer event, why?
« Reply #14 on: September 06, 2024, 10:00:21 am »
I explained that above. Basically there is no parent window that can listen to messages.
On windows, that is a problem.
There is nothing wrong with being blunt. At a minimum it is also honest.

 

TinyPortal © 2005-2018