Events

An Object, generated by Delphi, has properties (we saw last week), events and methods. From the object oriented programming (OOP) principles, if we like to do anything with an object, we have to ask him to do itself. The procedures, which manipulate with objects, are so called methods (and we can call them, when we like). By contrast, "events" are the procedures as well, but we have to write them, if we like to handle an appropriate event. The most often used event handler can be generated by a double-click to a grafical representation of an object. For the rest of events, we have to select them in the Object inspector (envoked by the F11 key) by the doubleclick in the appropriate line (where is the value, if we display properties fold). For example, for a button most important (so "default") event is, that anyone will click on it (that is, why a button is here). If you like to serve for example to move of a cursor, we have to find this event in the Object inspector (in this case, onMouseOver, the default is onClick). (button can become red, if you move a coursor over). The list of events rely on the object.

Timer

If you like your program not to do everything immediately, but regularly, you can use a Timer. Typical use is some kind of animation (in a graph drawin, for example), another use could be some device control (but the E371012 subject, where you can study it, is not opened in English now, see here). Sometimes we need regularly check, if in some directory is a new file, or some file on a web address. In these cases, the Timer component can help us. This is placed on the System toolbar.

As the first example, we can try to write a program, which will increase a number, displayed on the Label1 component, by one. We can start with an empty form, where we will place this two components (Label and Timer). For the Caption of the Label1, please set the zero (0) as a start value (using the Object Inspector). To manipulate with it, you need to convert the text (Caption is the "string" type) to the numerical type. For this, Delphi has two function:

StrToInt(s) - converts string s to integer. If it is not possible, ends program with an error message.

IntToStr(s) - converts integer value to a string.

We have to convert the value written in the Caption of the Label1 to integer first, than increase it by one, then convert back; the result will be string, and can be assigned to the Caption property back. Note, that all conversion can be only on the right side of the ":=" sign:

Label1.Caption:= IntToStr(StrToInt(Label1.Caption)+1);

Now, set the Interval property for Timer1 to some reasonable value (the value is in miliseconds, you can try for example 333) and check, if the Enabled property is set to True (False means "no events will be generated").

Note1. Variable with only two possible values (True, False) is so called logical; in the Pascal, name of this type is Boolean, in literature written with the capital B (name of a person), for example:

var
  b : Boolean;

The True and False are constants; there are not possible to assign anything else to this type of variable. The result of a compare command is one of this values.

Note2. If an timer should generate a new event before the previous is completely served, he skip this. If you make the interval too short, you can loose some events.

Note3. The Timer component disappear after program execution, because it has no graphics representation (and this would be useless at all). There are many components with this behavior.

Another task (no solution here):

Write a program which starts with some reasonable number (30?) to zero.

Napište program, který počítá od nějakého rozumného čísla k nule. The task is simple; another problem is, that the program should stop after reaching the zero. Postpone it for a while:

Logical expression

Logical expression is any expression, which result will be just True or False. A condition is a good example:

a < b a is less then b
a > b a is greater then b
a <= b a is less then equal then b. Note, that there are no space between < and =.
a >= b a is greater or equal then b. Note the signs order.
a = b a and b is equal. Only use of separate = sign in the Pascal.
a <> b a and b is not equal, or is different

You can connect more condition by this operators:

and  is True only in the case if the both operands are True.
or  is True if at least one of the operands are True.
xor  is true, if the operands are not the same (nonequivalence)
not unary operator - switching the parameter value (as a minus (-) sign in aritmetics)

The main problem in the Pascal is, that the same operators are used for work with integer values. This results in priority problem:

Priority

If any expression is evaluated, expression is solved by priority, or from the left to right if the same; the priority levels:

parenthesis, ()  the highest priority
unary operation not, negativ sign for a number; function evaluation (with parameter(s) in parenthesis)
multiplication level * / mod div and
sum level + - (as an subtraction of two numbers) or xor
comparison < > <= >= <> =

Not all operator listed. For example, the expression

a < b + 9

will be evaluated that way, that the sum (b + 9) will be done as the first, then compared with a, which is probably the anticipated result.

If we write

a>3 and a<7

, than the and is evaluated as the first (see priority table), resulting with nonsense (condition with three operands). We have to use parenthesis:

(a>3) and (a<7)

If you are use more parenthesis then necessary, nothing happend, the Borland compiler will useally create the same code.

Conditional statement

... in the Pascal has the next form:

if condition then command else command;

The else part is optional:

if condition then command;

Note: the else part is just a part of the same command, and there are no semicolons inside a command.

The program, which should stop at zero, can use this code:

Form1.Label1.Caption:= IntToStr(StrToInt(Label1.Caption)-1);
if StrToInt(Label1.Caption) = 0 then Form1.Close;

The Left and Top properties

As an example of animation we can try to change the values of properties, denotes the position of an object. The Left and the Top means the distance of upper left corner of an object from the upper left corner of the active area of the form (exclude the frame and the heading of a window - so called Client area, see the ClientWidth and ClientHeight property of the form).

We can move the button to right by the command

Button1.Left := Button1.Left + 1;

If you write this to the Timer1.OnTimer event, then the button will very slow (at least with the Timer1.Interval being set to333) move right. If this reach right border of window, the horizontal scrollbar will be generated and the button will disapear from the actual visible area. To solve this, the button should turn back at the border of the window. We will need this properties:

Form1.ClientWidth with of the active area of the form
Form1.Button1.Width with of the button itself
Form1.Button1.Left actual position of the button

During move to right, we have to check the following condition regularly:

Button1.Left + Button1.Width < Form1.ClientWidth

While mowing back, the only condition is, that the button position should be bigger then zero.

In the real situation, we should keep some distance from the border. I would reccomend bigger then the step of the move. In the following solution, step will be 3 and this zone 8.

Speed - if the button should move fast, the maximal reasonable value is about 50 ms (20/s, more cannot be seen by user). Instead moving more often, you should make step bigger.

We need at least one global variable to keep a direction; the Boolean (logical) variable is enough. If the variable declaration will look like (the grey code was there before we inserted it):

var
  Form1: TForm1;
  smer : Boolean;

implementation

, then the whole event solution can look like this:

  if Button1.Left + Button1.Width
        > Form1.ClientWidth - 8  {the right limit touched}
    then smer := False;          {True = right}
  if Button1.Left < 8      {left limit touched}
    then smer := True;
  if smer then Button1.Left := Button1.Left + 3
          else Button1.Left := Button1.Left - 3;

Checkbox

The Checkbox component can be either checked or not. This is written in the Checked property (logical). We can use this component to keep a value of direction; the advantage is, that we can change this value using mouse during program running.

Add a CheckBox1 component and change the program to use this:

  if Button1.Left + Button1.Width
        > ClientWidth - 8               {the right limit touched}
    then CheckBox1.Checked := False;    {True = right}
  if Button1.Left < 8                   {left limit touched}
    then CheckBox1.Checked := True;
  if CheckBox1.Checked then Button1.Left := Button1.Left + 3
                             else Button1.Left := Button1.Left - 3;

Now, we are again without variables. In many cases, mainly when debugging, we can keep most of variables in visual components. It is slow, but can be very transparent when use.

The last task for self-study:

Add two Edit boxes so you can change the Inteval property of the Timer1 and the step (size of move, "3" in the previous example).

Solved program (of which there are the source) is here.