How to handle OnTimer event in Delphi XE2? - delphi

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.

Related

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 Timer: Time before next event

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.

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.

i need help in delphi-7

i was wondering if anyone could by any chance help me. i have a school project due in 10 days and to be honest i have no idea what im doing =/ what is expected of me is that i program a memory game. where i am currently stuck is that i have to time how long it takes the person to play the game and then display how long it took them as a 'score' at the end. how do i time? what component should i use and how do i program this component to time? it should start when a button is clicked and then end when the game finnishes. any help will be highly appreciated!
Why don't you save the current time in a variable when he starts the game, and again save the time when he ends?
You can take it by the Now instruction.
var time: TDateTime;
begin
time := now;
ShowMessage(DateTimeToStr(time));
end;
You'll see the current time in the system.
You will need
1.- In your form,
add a timer, and set its Enabled property to False.
add a label to display the time
add a private attribute startTime to record the time when the user starts the game.
should result something like this...
type
TForm1 = class(TForm)
...
Label1: TLabel;
Timer1: TTimer;
...
private
startTime:TDateTime;
....
2.- At the click event of the start button, the code to initialize the startTime attribute and kick-off the Timer.
procedure TForm1.Button1Click(Sender: TObject);
begin
startTime:=Now;
Timer1.Enabled:=True;
....
end;
3.- At the Timer event of the Timer, some code to display the time counting
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Label1.Caption:=TimeToStr(Now-startTime);
....
end;
4.- At the click event of the finish button, or when the program considers the end of the game, some code to stop the timer.
procedure TForm1.Button2Click(Sender: TObject);
begin
Timer1.Enabled:=False;
Label1.Caption:=TimeToStr(now-startTime);
....
end;
PA's answer seems to be exactly what you need. because if i understood well and this is your first time working with delphi, i'd only add that:
Now is a function defined in SysUtils that returns the current date&time
you'll find the TTimer on the System component pallette (see image in link below)
all the procedures where you need to write the code will be automatically generated by selecting the Events tab in the Object inspector, and then double clicking in the input box (see image in link below)
http://i.stack.imgur.com/0iNsL.png (sorry, can't inline images because i don't have the necessary reputation yet)
from here on it hould be very easy to finish your application
good luck,
G
Create a variable (for example StartTime) of TDateTime type in your form.
When the user starts playing, set the variable to equal Now().
When the user finishes, calculate the value of Now()-StartTime. The result is a decimal number that represents the fraction of a day that elapsed between the starting time and ending time.
To convert that to the number of seconds, multiply it by (60 * 60 * 24) (which is the number of seconds in a day). From there you can display the number however you want.

Detect if an application is not in use

How can I detect if an application is not used for more than x minutes in DELPHI
If you write Windows app take a look at GetLastInputInfo function.
Here is some code that looks for mouse and keybord activity with the applicatin
procedure TUserActivity.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
Handled := False;
case Msg.message Of
WM_KEYDOWN,
WM_LBUTTONDOWN,
WM_MBUTTONDOWN,
WM_RBUTTONDOWN:
Activity := TRUE;
WM_MOUSEMOVE:
begin
if (LastXYPos <> Msg.lParam) then
Activity := True;
LastXYPos := Msg.lParam;
end;
end;
end;
Use the Application.OnIdle event:
Write an OnIdle event handler to perform special processing when an application is idle. An application is idle when it is not processing code. For example, an application is idle when it is waiting for input from the user. 
OnIdle is called only once, as the application transitions into an idle state. It is not called continuously unless Done is set to false. Applications that set Done to false consume an inordinate amount of CPU time, which affects overall system performance.
Use either a timer or GetLastInputInfo as #aku suggests in this event to determine if you can start your maintenance without interrupting the user
Use the applications OnDeactivate and onActive events..
That way you can abort the longrunning job if the user activates your program again.
ex:
Application.OnDeactivate = yourDeactivProcedure;
procedure mainform.YourDecativateProcedure (sender : tObject);
begin
// do your job..
end;
To handle the activate event to abort you either have to do it a bad way with a sleep and after the sleep check if i global vairable has been set.
Or you can have a theared object that does the loongrunning job.
Which I would say is much better. You can set the loongrunningjobs priority to low and it wont affect your program as much,
Depends on how you're defining "used" -- if you were monitoring yourself, you could look at the last time you responded to user interaction by logging it when it happened (mouse move/key pressed/menu event fired/etc.). Monitoring another application is tricky as it'll be harder to define that it is "in use".
That really depends on the application and what it does. While users may not interact with it in the sense of new input, they certainly might be viewing the client area that is visible.
Also - you don't say if you want to detect this internal to the app or external to the app.
Simple methods
see if it has current focus.
check if the window is visible
lots of others too, but they rely on the app itself.
You must define what you mean by "used" as well. It could mean different things and that would make significant changes to how you determine whether it met your criteria or not.

Resources