Delphi Timer: Time before next event - delphi

Is it possible to determine when a TTimer in Delphi will trigger? I know how to calculate this based upon the timer's last run and the timers interval. Unfortunately, the code I am working with has many states and the interval can change in many ways. I would prefer to not have to keep track of when the timer was last enabled and the interval changed, but instead directly access this information from the timer.
Scenario: A timer has a 2 minute interval, 30 seconds have elapsed since it was last enabled, how do I find out in code that in 90 seconds the timer event will trigger again?
Is it possible to get this information from the timer directly? Or perhaps the OS? The timer component must "know" when it will be triggered next. How does it know? Is this information I can access?

There's absolutely no way to query a Windows timer for information of this nature. You will simply have to keep track of this yourself.
I would do this by wrapping up the TTimer with composition and not inheritance. You can then be sure that you will capture all modifications to the timer state.

In this case I recommend you switch from a TTimer which uses Windows timers, to a thread based TTimer-style component. Then you can query the time until the next event.
Alternative; If you want a simple ugly hack, then change your Timer interval to 1 second instead of 120 seconds, and do a countdown yourself:
const
CounterPreset = 120;
...
procedure TForm1.FormCreate(Sender:TObject);
begin
FCounter := CounterPreset;
Timer1.Interval := 1000;
Timer1.Enabled := true;
end;
procedure TForm1.Timer1Timer(Sender);
begin
Dec(FCounter);
if (FCounter<=0) then
begin
DoRealTimerCodeHere;
FCounter := CounterPreset;
end;
end;
function TForm1.TimeLeft:Integer;
begin
result := FCounter;
end;
This will be inaccurate subject to the limitations of the WM_TIMER message, documented only vaguely at MSDN here. Real experience shows that WM_TIMER should only be used for things that don't need to happen at all, and should be used as a convenience, not as a hard-timing system.

Related

How do i start Ttimer delphi, make the program wait 5 sec

how do I start Ttimer?
timer.interval := 5000;
lbl.caption:='1'
*wait 5 sec*
lbl.caption:='2'
I have ttimer in my form and it is enabled.
Set the timer's Interval property to 5000. That is the interval in milli-seconds.
Declare a private field of the form, FCount of type Integer.
Attach on OnTimer event handler to the timer:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
inc(FCount);
lbl.Caption := IntToStr(FCount);
end;
When you want to start the timer, initialise FCount and set Timer1.Enabled to True. Set Timer1.Enabled to False when you want to stop the timer.
I am assuming, based on your now deleted earlier question, that you want the counter to keep ticking.
Timers work by calling their OnTimer event every time they tick. Because each tick is a distinct call to the event handler, you have to store any persistent state somewhere other than local variables. That's because local variables only endure for the duration of their owning method. Hence the use of a private field of the form to maintain the count.
Note that a timer does not make the program wait. Timers are asynchronous. The program will still be responsive to user input while the timer is active. When the timer expires, the system synthesises a timer message in your message queue. The program does not block on the timer. So your UI elements will, unless you take action otherwise, still be enabled and responsive.

How to handle OnTimer event in Delphi XE2?

I need to show a message say "Hi" everyday at 9 AM. Do I require Timer for this? How can I check whether its 9 AM or not. What should be the interval of timer at which OnTimer event run?
procedure Form1.TimerTimer1(Sender: TObject);
begin
ShowMessage("Hi");
end;
If I run this event in after 24 hours, I fear it might pass 9 AM and will not fire.
Unless you have other valid reasons, it's far more easier to
write a simple application that shows the message and quits
schedule it to run using the task scheduler of Windows
You can use CRON like solutions for Delphi: http://www.cromis.net/blog/downloads/cron-scheduler/
As the answerers before me said, there are better and easier ways. Suppose you want to do this your way, in Delphi, then yes, you need a timer. The needed steps are:
Put the timer on your form;
Set the "interval" property to 1000 (one second) or less. For greater precision, you can set the interval property to 1, and the program will do the checking each milisecond
Write the handler for OnTimer:
procedure Form1.TimerTimer1(Sender: TObject);
var x:TDateTime
begin
x:=Now;
if {the hour read is 9 and minute is 0} then
ShowMessage("Hi");
end;
Hope it helps.

Set timer to pause

How can I pause TTimer in Delphi with keeping interval? So, for instance, I have TTimer that has 10 seconds interval and when I set timer to pause after first 7 seconds of working, it will save its state, and then when I resume timer it will fire after remaining 3 seconds.
Thanks a lot guys!
Timer.interval :=1000;
ICount:integer;
In create procedure set icount to 0
Procedure timerevent(sender:tobject);
Begin
If icount=10 then
Begin
// do proccess
Icount = 0;
End;
Inc(icount);
End;
Now you can stop the timer anywhere
You cannot do that with a TTimer which is a loose wrapper around the SetTimer API.
In order to do this you would need to keep track of when the timer started and when you paused it. Then you would know how much time was left. When you need to pause, set the timer Enabled property to False and set the interval to be the amount of time remaining. Don't forget that after the timer fires for the first time you need to reset its interval to the true interval.
As you can see from the above, a TTimer is not the best fit for your problem. But, having said that it would not be terribly difficult, and quite fun, to produce a TTimer variant that supported pausing the way you desire.
That's not how the Windows timer facility works, and that's not how the Delphi wrapper works, either.
When you disable the timer, keep note of how much time was remaining before it was due to fire again. Before you re-enable it, set the Interval property to that value. The next time the timer fires, reset Interval to the original value.
TTimer does not support what you are asking for. As others have already commented, you would have to stop the timer, reset its Interval to whatever time is remaining, start it, then stop and reset its Interval back to 10 seconds when the next OnTimer event is triggered.
A simpler solution is to just let the TTimer keep running normally, and have a separate flag that tells the OnTimer event handler whether it can do its work or not whenever it is triggered, eg:
var
Boolean: Paused = False;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if not Paused then
begin
// do proccess
end;
end;
procedure TForm1.PauseButtonClick(Sender: TObject);
begin
Paused := True;
end;
procedure TForm1.ResumeButtonClick(Sender: TObject);
begin
Paused := False;
end;

Delphi idle handler only fires when I move the mouse

I have an OnIdle handler in my D2006 app. With this code:
procedure TMainForm.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
Inc (IdleCalls) ;
Sleep (10) ;
Done := False ;
end ;
the app runs smoothly, the idle handler is called 100 times per second, and the CPU usage is next to zero.
I then added a TActionList and connected up some controls to actions, coded an Execute and Update handler.
procedure TMainForm.ActionNewButtonExecute(Sender: TObject);
begin
DoNewProject ;
end ;
procedure TMainForm.ActionNewButtonUpdate(Sender: TObject);
begin
ActionNewButton.Enabled := AccessLevelIsSupervisor ;
end;
Problem. The OnUpdate event doesn't fire. On a hunch I set Done := true in the OnIdle handler and the OnIdle handler is then only called when I move the mouse. And the Update action still doesn't fire.
Why might the Update handler not be firing, and should I set Done to true or false? Or both?
Use the source, Luke. :)
Look at the Forms unit, specifically TApplication.Idle. It contains, in part, the following:
Done := True;
try
if Assigned(FOnIdle) then FOnIdle(Self, Done);
if Done then
if FActionUpdateDelay <= 0 then
DoActionIdle
// Excluded to avoid copyright violation
// See also the else portion, which contains (in part)
else
if IdleTimerHandle = 0 then
begin
IdleTimerHandle := SetTimer(0, 0, FActionUpdateDelay, IdleTimerDelegate);
if IdleTimerHandle = 0 then
DoActionIdle
end;
finally
// Omitted
end;
As you can see, DoActionIdle is only called when either Done = True and FActionUpdateDelay <= 0 or IdleTimerHandle = 0. DoActionIdle (also part of TApplication) is what calls UpdateAction. So if neither of the above conditions are met, TAction.OnUpdate is never called.
There's a separate method, TApplication.DoMouseIdle, that you may want to peruse as well.
As mentioned in the comments, Sleep in the idle handler will do no good, also the bacground processing will stall if there is no activity on the application.
You can however lower the CPU usage w/o much disturbing effects: After processing all OnIdle events, the application will call WaitMessage (which will sleep while the message queue is empty), if the Done parameter is True - you can just unconditionally set it in your handler.
As for background processing, use either a thread and call back to the main thread via Synchronize or, if you really-really have to, use a timer and don't ever forget to handle reentrancy (both solutions will by the way wake the application even while WaitMessage).
Get rid of that OnIdle event handler, you accepted it is there just in case.
If you later need to perform background tasks, learn how to use threads. To get a specific frequency, you're allowed to use sleep or any other technique within a thread.
My advice is in this way because, as you see, that way of do things is interfering with other parts of your application. If it is a bug in the TApplication, I don't know, maybe it is. If you want to investigate more, make a copy of your project, check everything and if you think this have to work another way, fill a QC entry about that.
I was looking the XE source code and it seems Ok, they set an event to update the actions if the Idle event is not done.. I don't see a bug there. I have no pre-2010 ready installations to check ancient versions.

Timers In Delphi

Consider the following code
Timer1 .Enabled := False;
Timer1.Interval : = 300;
For I := 1 to NumberOfTimesNeed do
Begin
Timer1 .Enabled := False; //
Timer1 .Enabled := True; // reset the timer to 0.30 seconds
TakesToLong := False;
DoSomethingThatTakesTime; // Application.ProcessMessages is called in the procedure
If TakesToLong = True then
TakeAction;
End;
procedure Timer1Timer(Sender: TObject);
begin
TakesToLong:= True;
end;
Question :
When I disable and then enable the Timer1 with
Timer1.Enabled := False;
Timer1.Enabled := True;
Does this reset the timer ?
i.e. will it always wait 0.30 Seconds before timing out.
Yes, it will. Setting Enabled to False will call the Windows API function KillTimer() if the timer was enabled before. Setting Enabled to True will call the Windows API function SetTimer() if the timer was not enabled before.
It's a standard idiom, which has been working since the times of Delphi 1.
I would however implement your code in a different way:
Start := GetSystemTicks;
DoSomethingThatTakesTime;
Duration := GetSystemTicks - Start;
if Duration > 300 then
TakeAction;
which would work without a timer, and without the need to call ProcessMessages() in the long-taking method. GetSystemTicks() is a function I have in a library, which does call timeGetTime() in Windows, and which was implemented differently for Kylix (don't remember how, I purged that code long ago).
One further thing to be aware of is that timers are the lowest priority notification on the system. So if the computer is busy, which may include the app here doing its work, the timer may not trigger for quite a while. So it could be several seconds before the TakesToLong variable gets set to true, even though the timer is set to 300 milliseconds.
I would suggest reading up on threads. Long actions (I'm not saying 300 ms is long but it sounds like that it could obviously go longer than that) tend to freeze the GUI and lead to choppy applications. Now, you can throw application.processmessages in there to keep the GUI going, but it can easily throw off your intended procedural coding style.
I'm from the side of the aisle that believes Application.ProcessMessages should be banned unless you are a VB programmer trying to use Delphi to mimic DoEvents and you still haven't gotten out of the VB mindset.
Spawn a thread and do your work in that thread. If want to update the GUI side when you are done, call Synchronize to 'safely' do so. Communicating the ongoing status back and forth with the thread brings in a whole new conversation that is quite a leap from using the timer control though.

Resources