HOME - - - - - Delphi Tutorials TOC - - - - - - Other material for programmers
Delicious Bookmark this on Delicious    Recommend to StumbleUpon

Delphi tutorial: TTimer component, Event Driven Environments

This has good information, and a search button at the bottom of the page

Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

Click here if you want to know more about the source and format of these pages. It may be easier to read this if you re-size the window, so that it does not use the full width of your screen.


This is a simple little program, but it may help you become more familiar with an important aspect of working with Windows. The aspect arises because Windows is a multi-tasking, event driven environment.

As far as users are concerned, the application is a timer, like an egg timer. I am writing this because I want to have a egg timer on my desktop, not just to illustrate things for you! The user will specify a time interval, and after that interval, the computer will beep. (By the way: If you want to use this program 'for real', you may have to deal with issues arising from your screensaver and power save routines.)

The timer won't be very sophisticated. There will be an 'hours' edit box and a minute edit box. The user clicks a 'start' button, and the countdown begins.


This tutorial was edited heavily in May 2007, and re-tested using Delphi 2. Nothing here should be very different with any other version of Delphi. Set up a folder, as usual. It can have any name, be any place, but I used one called TM13 (My 13th TiMer program.)



Do File | Save All. First you have to answer the 'Save Unit1 As' questions. Make the file name TM13u1, being sure to save it in the directory you created for the project. Next you have answer the 'Save Project1 As' questions. Call it TM13. (Even without removing the suggested, selected name, you can just type "TM13"... you will already be in the right folder, and the .dpr extension will be added for you. You may have noticed a moment ago that when you went to make buStart's caption &Start, it had already been given a default name, "buStart", derived from the name you'd given the button. Delphi does a lot of things for you, if you let it, if you "go with the flow".)


Add two edit boxes. Name them eHrs and eMin. Don't change anything else about them, although you can re-position them (or other components) at any time.

Add a label. Call it laMinLeft.

Double click on an empty part of the application's the form. You will find that your cursor jumps into the TM13F1,PAS editing window, between the 'begin' and 'end' of 'procedure TForm1.FormCreate(Sender: TObject);', the shell of which Delphi has just helpfully created for you. (By the way... it will disappear if you run the program while the shell is empty. Even putting a single comment between the "begin" and the "end" will keep the shell in place if you want to keep it. And emptying everything between the "begin" and the "end" is the way to get rid of Delphi generated things that you don't want... don't try to delete the whole subroutine by hand... you will leave related "stuff" behind.)

Within the FormCreate procedure (between the "begin" and the "end") enter...
eMin.text:='10';
eHrs.text:='0';


This will make the program start set to run for 10 minutes.... but the user can choose a different time for it to run.

Remember to save your work from time to time. The icon showing a folder with an arrow to a rectangle, tooltip "Save All" is a quick, easy way to do what's needed. (The floppy disc icon would do for now, but eventually you'll be doing work that will entail more things.)

Now we're going to move on to a few things that may seem a bit weird at first. Get used to them... you'll meet them again and again in Windows's event driven environment. (Or if you program for Linux or a Mac.)

Double-click on the form's eMin edit box. This will create an empty shell for a procedure called eHrsChange. (You could also create or access this via the OnChange line on the Object Inspector's "Events" tab for the eHrs component.... which is a quick way to find things when your sourcecode gets lengthy.)

Enter the following, which looks strange, but is only a start! (We will ignore the eHrs edit box for a moment.)
if eMin.text<>'' then
laMinLeft.caption:=IntToStr(StrToInt(eMin.text));


What is all that?? It is something that will happen whenever the contents of eMin.text changes... for whatever reason. Windows will take care of "watching" eMin for you.

The....
if eMin.text<>''
... part is to stop the program having problems if you change the eMin text to be nothing. This isn't robust or thorough error trapping, but it is better than nothing. This is, after all, a beginner's level tutorial!

Always study odd code from the 'inside' out. In this case, the first thing to look at is the

StrToInt(eMin.text)

It turns a STRing (eMin.text) inTO a number (of type INTeger)... See where the name StrToInt comes from? (I usually just write it inttostr, which works fine, but is harder to read.)

However! laMinLeft's caption must be data of type string. (Why does an edit box have 'text', but a label has a 'caption'? Don't know.)

So... and the reason for this WILL become clear, I promise, we take the thing we made into an integer and turn it back into a string with IntToStr!

Save what you have. Run the application. Get rid of the inevitable typos. When the application is running, as soon as you change the contents of the eMin edit box, you should see corresponding changes in the laMinLeft label. You don't even have to press "enter" or anything to make the "echo" change.

Now revise the eMinChange procedure to make it....
procedure Ttm13f1.eMinChange(Sender: TObject);
var iMinsFromHrs:integer;
begin
iMinsFromHrs:=0;
if (eHrs.text<>'') and (eHrs.text<>'eHrs') then iMinsFromHrs:=60*StrToInt(eHrs.text);
if eMin.text<>'' then
laMinLeft.caption:=IntToStr(iMinsFromHrs+StrToInt(eMin.text));
end;//eMinChange


(Save again, before you run that.) Get the program running. All it should do so far is to get the laMinLeft caption to reflect the settings in the two edit boxes....when you change what's in the eMin edit box. Changes to the eHrs edit box will affect what you see when you change what is in the eMin box... but not until you make a change in the eMin box. We'll come back and fix this in a moment, but first....

The 'and (eHrs.text<>'eHrs')' part of the IF statement is needed because when the program first runs, the eMin.text:=10 in the form's OnCreate causes execution of the eMin OnChange event. At that time, eHrs.text is 'eHrs', which will cause StrToInt(eHrs.text) to complain! (There are simpler, and better, ways around this that I might use in real life, but while doing this Level Two tutorial doing it this way is sufficient.)

We also need an OnChange event handler for the eHrs edit box. It will be similar to the one we've just done. See if you can devise it before reading the answer below....

v

v

v

v
procedure Ttm13f1.eHrsChange(Sender: TObject);
var iMinsFromHrs:integer;
begin
iMinsFromHrs:=0;
if (eHrs.text<>'') then iMinsFromHrs:=60*StrToInt(eHrs.text);
if eMin.text<>'' then
laMinLeft.caption:=IntToStr(iMinsFromHrs+StrToInt(eMin.text));
end;


So far so good! We're using events! But in fairly simple, obvious, ways.... so far!

This would be a good time to take a break!




Put a 'Timer' on your form. You'll find it on the system tab of the Component Palette. (It looks like an old fashioned clock face. It can go anywhere on the form, because you won't see anything when the application is running.) Delphi's name, Timer1, will do fine.

Add....
Timer1.enabled:=false;
Timer1.interval:=600;
....to the FormCreate procedure. That interval of 600 is a cheat... your clock will run many times faster than it should... which will make testing easier!

Double-click on the timer, to start the Timer1Timer procedure. Put....
laMinLeft.caption:=IntToStr(StrToInt(laMinLeft.caption)-1);
... between the "begin" and "end",

Double-click on the button called "Start" that you put on the form. In the resulting buStartClick procedure, put....
Timer1.enabled:=true;
... between the "begin" and "end".

Don't expect the application to "work" yet, but "run" it, just to learn of any typos. You should get the application appearing on the screen, and changing what's in either the eHrs or the eMin edit boxes should affect what's in the laMinLeft label. The clock just doesn't run... yet.

The timer is a pretty primitive component: From the Object Inspector, we can see that it has only four properties and one event, and during the program's execution nothing visible appears on the form. Even so, it has it's uses!

Double-click on the timer to start the Timer1Timer procedure. Just as a start, insert....
laMinLeft.caption:=inttostr(54321);
... between the "begin" and "end".

... save your work, run it. Once you clear up the typos, if you click on the 'Start' button, you will see the number in laMinLeft become 54321 almost immediately.

Once that's working, revise the line you just added, making it.....
laMinLeft.caption:=inttostr(strtoint(laMinLeft.caption)-1);
Note that all we have only replaced 54321 with something more sublime. Study it; make sure you grasp what is going on.

What is going on? I'll tell you..... But first a little aside about terminology....

With the help of Delphi, we created the procedure "eHrsChange". (Don't worry about the "Ttm13f1.....(Sender: TObject)" stuff around that just now. They are stories for another time.) We are using that procedure to "handle" the event that happens when the contents of the eHrs edit box changes. Thus that code is called an event handler. Now then. Every edit box triggers an event every time the contents of that edit box change. Those events are called "OnChange" events. The event handler for eHrs's OnChange is eHrsChange. Don't be perplexed when you see references to event handlers by the name of a particular handler sometimes, and by the name of the event being handled other times. You can use the Object Inspector to see all the events of any component, and to see what handler has been assigned to kick in if a particular event occurs. (Many events are assigned no handler. This is okay.) (You can even have situations where, say, the OnChange event of a number of edit boxes are all being handled by the same event handler.) So! On to why and how our application works.....

Whenever the timer's enabled property is set to true, the timer is counting down from a starting value. When the timers count reaches zero, an "OnTimer event" is triggered. In our program, we set have that up so that when the OnTimer event occurs, the application looks at the number in laMinLeft.caption, and replaces it with that number minus 1. As long as the timer is still enabled, after the OnTimer event occurs, the timer goes back to it's start- from number, and starts counting down again.

The timer's interval property determines what number it starts its countdown from. We used the OnFormCreate event handler to set Timer1's interval property to 600. Therefor, when the timer is enabled, an OnTimer event occurs every 600 milliseconds. To make the egg timer work in minutes, make Timer1's interval 60000. Setting it to something like 600 when debugging the application saves you having to wait up to a minute to see anything happen, and several minutes to see the bit of the application we haven't got to yet: What happens when the egg timer has timed however many "minutes" (real or debugging minutes) it was set to time.

To (mostly) finish the application, you only need to add 4 more lines to the OnTimer handler, after the line already there, insert....
if laMinLeft.caption='0' then begin
timer1.enabled:=false;
tm13f1.color:=clRed;
end;//for...


That's it! The program now does almost everything it was supposed to! I apologize for the failure to have it make a noise at the end of the timed period, but that is more trouble than you might expect! There are notes about it in the Level Two 'How To.. section. You "only" have to put the code for "Make a noise" into the program next to the
form1.color:=clRed;
line if you want to have the noise.


I hope that you have extended your grasp of what an event driven environment is all about? That's critical. Also, I hope you understand the timer component better? (An application can have several timers, by the way.)

If you programmed in "the good old days", you should note that it is a Very Bad Idea in Windows to have something like
for c1:=0 to 10000 do sTmp:='pause a moment';


You shouldn't create loops which will tie up the system, especially loops which are only there to create an idle time during the program's execution. Any long loop should have
application.processmessages
in it, to allow Windows to keep up with various housekeeping. For example, if you were adding all the numbers between 1 and 1000, you'd do something like...
var wC1,wCount:word;
begin
wCount:=0;
for wC1:=1 to 1000 do begin
wCount:=wCount+wC1;
application.processmessages;
end;
end;

Next, some odds and ends you can skip over, if you like....

Just before we finish, there's another issue you may want to know about.

If you do...
Timer1.enabled:=false;
Timer1.enabled:=true;
... then if Timer1's interval is set to 500, then 500 milliseconds later, the timer's event will be triggered. I am not aware of any easy way to see how much of the interval has passed before a moment when you disable it. I am not aware of any easy way to use the unused part of a previous, prematurely halted, countdown.

If you want, say, to give a pupil 30 minutes for a task, but to provide a 'pause' button which will 'stop the clock' if necessary, then the way to do that would be to use a timer to time the passing of minutes (or a smaller unit). You'd use the TTimer component to measure, say, minutes, but accumulate the "minutes worked" figure in an ordinary variable.

If you look at the contents of the timer's interval property, it will always give you the full time of one timeout, regardless of how recently the timer was last enabled. In other words, the number reached during the countdown is held elsewhere, in some other place, which isn't available to you. (afaik!)




That's it for this tutorial. I hope you found bits useful. The application has many faults... for example, if you click start a second time after the first period expires, the application doesn't behave.... but it does show what it was meant to. Fix the shortcomings as an exercise to strengthen your skills even further!


            powered by FreeFind
  Site search Web search
Site Map    What's New    Search This search merely looks for the words you enter. It won't answer "Where can I download InpOut32?"


Click here if you're feeling kind! (Promotes my site via "Top100Borland") Looking for email, domain registration, or web site hosting? If you visit 1&1's site from here, it helps me. They host my website, and I wouldn't put this link up for them if I wasn't happy with their service. They offer things for the beginner and the corporation.


Ad from page's editor: Yes.. I do enjoy compiling these things for you. I hope they are helpful. However... this doesn't pay my bills!!! Sheepdog Software (tm) is supposed to help do that, so if you found this stuff useful, (and you run a Windows or MS-DOS PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.

Link to Tutorials main page
How to email or write this page's editor, Tom Boyd


Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....