HOME - - / - - / - - LAZARUS/ DELPHI TUTORIALS
LAZARUS/ DELPHI COURSE - - / - - / - - Other material for programmers

Lazarus and Delphi Course: A simple converter: Inches to centimeters...

Page URL: Conv1.htm

This page is information rich, and a has search button at the bottom of the page.

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



You will probably find the text easier to read if you make your browser window much narrower than usual. You may also want to change your browser's zoom level, to enlarge the text. Opera (at least) lets you change zoom level easily. The text will adapt nicely to the settings you decide give the best results for your needs!

The lines of sample Lazarus/ Delphi code in these pages will not "wrap". I.e., if a line is too long to show in the width you have set your browser too, parts of the line will be "off the page". Those lines will still copy/paste properly, at least in Opera. Please feel free to send feedback on the choices I've made! (Will you forgive me for not forcing upon you a column of links on the left and a column of ads on the right?)

This is just one exercise in a series of Lazarus / Delphi exercises. You will probably be best served by doing them in sequence... each assumes some prior knowledge. Material © TK Boyd, sheepdogsoftware.co.uk, 4/05-11/20.

Heavily re-worked, June 2020, to bring it up to date for Windows 10/ Lazarus 2.0.0 Based on earlier Delphi page. Probably still useful to Delphi programmers, too.


In this lesson, you make an application to convert between feet and meters. The point is in the way the application works... I realize that converting between feet and meters isn't your most pressing need! The lesson, I hope, reinforces previous learning.

You should learn how to...
--- use the OnChange event

You should learn about....
--- What "event driven environment" means
--- Arithmetic within Lazarus/ Delphi
--- The concept of "parameters" for functions and procedures

Pascal: the language behind both Lazarus and Delphi:
--- More data types: real, floating point, single

Lazarus/ Lazarus/ Delphi provided functions:
--- FloatToStringF
--- StrToFloat


This lesson will produce a small program which will convert lengths in inches to lengths in centimeters.

Our initial "solution" will be very similar to the "database" program in the lesson "Very simple data lookup".



Now you need to save what you have so far. If you haven't studied my "Where To Put Things" guide) yet, you really ought to put this application (program) to one side for now, and work through that first. If you have worked on it, I hope the following will not surprise or perplex you...

In whatever folder you are using for your Lazarus work. Call it "DT100 examples", because you are working from something in my site's dt100 folder.

Create a sub-folder in DT100 for JUST this example. Call that folder conv1-ConvertInchesToMeters.

Within THAT... create a folder called conv1, and in THAT, save what you've got so far...

Save the project as conv1.lpi, if you are using Lazarus. (conv1.dpr if you are using Delphi.)
Save the unit as conv1u1.pas.

---------- Moving on...
Rename the label laOutput.

Make the initial text in the edit box nothing by clearing everything in the "Text" property of the object, after you've re-named it. (Until you have changed the name of an object once, the caption will keep changing to be whatever the name is. Often, it makes sense to leave the caption (or similar) the same as the name, so that as you work with the form during design, you can see the object's name on the form.)

Rename the button "buConvert".

Make its caption "&Convert". (If you put the & in front of the button's caption, you won't see anything when the application runs, but it will mean, in this case, that pressing alt-C will be as good as clicking on the button. (In "the good old days", the "C" would have come up underlined. Can anyone email me with the answer to making that happen in the Lazarus of 6/2020?)

Now we're going to make what happens when you click buConvert Do Something.

On the form designer, dbl-click on buConvert. That will put a "skeleton", an "outline" into the source-code. (And it does a few things you can't see so easily at the same time. Crucial things. Things you don't need to know about, but you do need to know that JUST doing the following isn't enough. Once you've done that....

Make the button's OnClick....
procedure Tconv1f1.buConvertClick(Sender: TObject);
var singQuantity:single;
    sTmp:string;
begin
try
  singQuantity:=StrToFloat(edit1.text);
  laOutput.caption:=FloatToStrF(123.4,ffFixed,7,2);
 except
  laOutput.caption:='Could not convert';
 end;//try.. except
end;//buConvertClick
Warning! We are about to enter some Heavy Weather. Get yourself through it, and I hope you'll find the last part of the tutorial refreshingly easy. And you will have some important ways of looking at Lazarus/ Delphi programming under your belt.

Run this. Click on the "Convert" button.

In Lazarus, you should get...

-

Resist the temptation to tick the "ignore this exception type". For the rest of the time you are working on this exercise, just avoid putting something in the edit box that isn't a number! Then you won't see the error message.

What's the use of that? Well... two things: First of all, when a user uses the result of you're programming, they will start the application by double-clicking on the .exe file, or using a shortcut to it. If you start the program that way, you won't get the error message... even if you put a non-number in the edit box. (Try that now, if you like... go to the folder where you are working using File Explorer, and double click on the .exe file.)

Secondly... if you tick the "ignore this exception type", you may be setting Lazarus to never tell you when it comes up. And then, when it is coming for some problem you have not provided for, you will find detecting and fixing that problem much harder.

So Just Don't Do It.

Click Continue

The label should now read "Could Not Convert"... but you may not see that immediately. If you are looking at....

-

... then the Form Designer window is "on top of" the running instance of your app. Notice the grid of dots across the unused parts of the form? That tells you at a glance that you are looking at the form designer.

Down on your task bar, there should be (Windows 10) an icon for the running .exe. Click that to bring it's window to the top.... -

Save what you have so far. (I won't keep telling you this.)

Digression...



Click the "x", upper right on the window of the running app, your little converter.

Go to the form designer window. Double-click somewhere on the field of dots.

That will take you to the FormCreate event handler's code....

-

Make it...

procedure Tconv1f1.FormCreate(Sender: TObject);
begin
  top:=20;
  left:=60;
end;//FormCreate
... and compile the program again. Click the "Covert" button. Click "Continue" when you get the EConvertError message again.

See how the change you made to the FormCreate handler determines where the app opens? (By all means "fiddle" with the values for top and left. They set where the upper left corner of the window will be. As in all Windows (and other OS's, I believe) matters, the UPPER left of the screen is called x=zero, y=ZERO.

Now we are past that problem of the form designer hiding the running applications, and vice versa.

Digression ends!

Do a quick, easy thing before we go on. Add the following to the FormCreate event handler...

Edit1.text:='999';
The "text" property of an edit box is like the "caption" property of buttons and labels. The new line, in the FormCreate handler, will "set" the property to '999' as the application begins to run.

You should be able to enter things into the edit box, but clicking convert will always result in "123.40" appearing in laOutput.caption, as long as you enter something that looks like a number. If it doesn't, you know what will happen. (If you don't, try it!)

Always responding with 123.40 isn't what we wanted as our ultimate achievement, but it is all we've told the computer to do so far! Always build your applications a little at a time.

Always remember that it only does what you've told it to do, according to the actual rules of the language. It has no idea of what you wanted, or what you think the rules of the language are.

----

Believe it or not, it took me about five minutes to get even that much "right". Part of the art of programming is learning to break the development task down into parts, so that you can build your application up gradually. In a little starting point there are only a few bits and pieces, only a few places to look for the mistakes you've made. Get the starting point working. Add a small bit. Get that working. Add a small bit...

----

One of the Great Secrets of programming is a skill, not a fact. I can't tell you "the answer" to "how should I build this". You have to learn the skill of always remembering where you are going, but of finding a way to start small and build towards your want.

----

I suspect the "try... except... end" thing is a little daunting?

Break it down.

First of all, as I just said, it consists of...
try
(STUFF)
except
(STUFF)
end
... and there must be the usual semicolon at the end of "the statement".

Yes! ALL of that (from the "try" to the "end", inclusive) is ONE STATEMENT. Lazarus and Delphi are both IDEs for working in variants of Pascal. All (almost all?) Pascal code is a bunch of statements separated by semi-colons.

Where you see "stuff" above, there will be another statement. That statement is simply nested inside the "outer" "try... except... end" statement.

And a very simple rule that opens up a world of possibilities is...

Two statements with a semicolon between them "boil down to" (count as) ONE statement. (When you use this trick, the result is called a "compound statement". But it is still "a statement", just a certain sort of statement.)

Ah, yes, but what's a statement? You'll learn these as you go along.

try [statement] except [statement] end

is a statement.

top:=20

is a statement.

Now... stay with me here, it's weird....

There'd be little point in the following, but if you think about it in steps, the rule above says that the following boils down to "a statement"....
top:=20;left:=60;top:=20;
How do I get that?? Why do I say that is "a statement"?

top:=20;left=60;

is clearly "a statement" according to the rule. By the same rule then...

top:=20;left=60;;laEdit.text:='999';

must also be "a statement"... it is still "two statements" with a semicolon between them, isn't it?

Why does it matter?

It matters because you will not always be doing Lazarus like this. Soon you will need to work from the ordinary documentation of the language. For example...

https://wiki.freepascal.org/IF (You don't need to go there immediately.)

Tells you how the "if... then... else" statement works. (The "... else" bit is optional.)

Here's an extract from that...

The IF statement allows you to branch based on the result of a Boolean operation. The one-way branch format is:

if BooleanExpression then
  StatementIfTrue;
  
If the Boolean expression evaluates to true, the statement executes. Otherwise, it is skipped.

The IF statement accepts only one statement. If you would like to branch to a compound statement, you must use a begin-end block to enclose the statements

(extract ends.)

Gobbledygook.... unless you understand "statement" and "compound statement"!

----

Returning to "try... except... end"...

When a program's code includes one of these, it starts trying to do the stuff after "try", in the case of our program, that would be...
  singQuantity:=StrToFloat(edit1.text);
  laOutput.caption:=FloatToStrF(123.4,ffFixed,7,2);
... which probably looks a little arcane to you at the moment, but, if all goes well, it will change what the user sees in the label we named laOutput.

Try remming out the "singQuantity" line, and making the laOutput line REALLY SIMPLE...

  //singQuantity:=StrToFloat(edit1.text);
  laOutput.caption:='REALLY SIMPLE';//FloatToStrF(123.4,ffFixed,7,2);
Run the program again, and click the convert button. No surprises, I hope? Figure out what puzzles you, of there were any, and put the lines back as they were.

I said that when a program's code includes a try... except... end, it starts trying to do the stuff after "try". When we had "Edit1" in the edit box, a problem arose when the program tried to convert that to a number. When a problem arises, the computer skips the rest of the code on the "try" block, and jumps to the code in the "except" block, does that.

-------------------

Back to the grind....

What do we have so far? We have the finished version of the form, populated with all of the controls it will ever have. I.e. what we're seeing on the screen now is what the final customer will see... it just doesn't fully "work" yet.... but that's okay.

We have started the OnClick handler which will make the application "do it".

We have the variables we need. They were declared just after the "var". The variable "sTmp", type string, should hold no terrors... we've made things like it before. The variable singQuantity needs some explanation.

I chose the name singQuantity as follows: The data is of type "single". The first part of the variable name, "sing", reminds me of the variable's type. The compiler won't care... I could call it XQ for all compiler cares... but the convention is useful. The second part of the name, "Quantity" could have been "Length"... as far as you know so far. As I intend to develop the converter beyond doing mere lengths i wanted to name this variable with a more general noun for "things you can measure".

I'll say more about the "single" data type in a moment. The prefix "sing" joins two you've already seen: "s", for "string" type data and "i" for "integer" type data. I always write the type reminder prefix in lower case, and write the first part of the rest of the name with an uppercase letter.

You (should) already know that string type variables can hold just about anything, e.g. 'abcde', '123', '2 turtle doves'... but it can't do much with it. You should also remember integer type variables. When you met them, you probably assumed that they were for holding numbers. That's right... up to a point. Integer type variables can only hold whole numbers, e.g. 1, -1, 1234. They can't hold "real" numbers, which is all the integers and numbers with fractional parts, e.g. 12.3, -45.65443.

Even that isn't the whole story... but it will suffice for now!

So... that explains the entries under var.

This application also makes use of the try... except...end structure. Look at the OnClick handler again, find those three words.

As I said... the application will try to do the things following the word "try". If all goes well, it will ignore from the "except" to the "end". If at any point during the "try" section there's a problem, the application will skip directly to what's in the Except section.

---
I said "if there's a problem". The problem we are likely to have is an attempt to convert something non-numeric into a number. If, for instance, the user puts "TeddyBear" into the edit box, how is the compiler supposed to turn that into a number?

The Lazarus/ Delphi-provided function StrToFloat is what will convert things like the string 456.8 into the number 456.8. "But!," you say, "They are the same!". To you and me... yes. To the computer, no.
sTmp:='456.8';//Notice the apostrophes
...would store the string 456.8 in sTmp.
singQuantity:=456.8;//no apostrophes
...would store the number 456.8 in singQuantity.

Look at the OnClick handler again. Read the first line from right to left. We start with edit1.caption... the string in the edit box. That is processed by StrToFloat, which changes it into a floating point number, which is a broad term covering type "single" numbers, among others. That number is then assigned to the variable singQuantity, i.e., singQuantity "becomes" the number which the string in edit1.caption represented.

Moving on to the next line....

The caption of laOutput will become a string. That string will derive from the number 123.4. FloatToStrF is always followed by four things (called "parameters") in one set of brackets. The first is the floating point number to be converted to a string, the second is a code for how the number should be written, the third and fourth have various meanings, depending on what the second parameter was. In the case of ffFixed, the third parameter should be a 7 for single type data, and the fourth determines how many places are given to the right of the decimal point. Run the application with the fourth number 1, and you'll get 123.4; run it with the fourth number 3 and you'll get 123.400.

Got that? Got indigestion? Hang in there. You're out of the Heavy Weather now! Take a break, if you need to.

Time to make the program work better. Rem out the old "laCaption:= line, and replace it, as follows...
try
  singQuantity:=StrToFloat(edit1.text);
  //laOutput.caption:=FloatToStrF(123.4,ffFixed,7,2);
  laOutput.caption:=FloatToStrF(singQuantity,ffFixed,7,2);
except
That should make your application "work" a little better! Whatever you enter into the edit box, provided it looks like a number, should be coming out in laOutput when you click buConvert!

(Important digression: Notice I name components a bit like I name variables? Labels all have names beginning with la, buttons with bu. If I wanted to re-name the edit box, I'd call it eInput. End of digression.)

A little mathematics for you: To convert inches to meters, you need to divide the inches number by 39.37 because there are 39.37 inches is 1 meter. To convert inches to centimeters, you divide by 0.3937

To make the computer do this for us, we need merely to add one line....
try
singQuantity:=StrToFloat(edit1.text);
singQuantity:=singQuantity/0.3937;  //new line
laOutput.caption:=FloatToStrF(singQuantity,ffFixed,7,2);
except
If that looks strange to you, you are reading ":=" as equals, aren't you? The new line actually says:"What's in singQuantity becomes what was in singQuantity previously, divided by 0.3937.

Congratulations! The converter is working! Your mother would say "That's very nice, dear." You father would say "But it's not working very well... why do I have to keep clicking that wretched 'Convert' button?"

Take a break. When we come back, will eliminate that annoyance.


Ummm... I lied. We got through Heavy Weather. We sailed calm seas with a good breeze. It's getting a bit strong again for a bit.

The secret to getting rid of the 'Convert' button is to tap into another of the events Windows provides and watches for. There's an OnChange event for the edit box. As it is the event you most often want to use, simply double clicking on the edit box will bring up the following OnChange handler starting point in the code editor. (Double clicking on a button brings up its OnClick event handler. Double clicking on an edit box brings up it's OnChange handler. Lazarus and Delphi quite cleverly take you to the event handler which is most commonly re-programmed for the sort of component you are double clicking on.)
procedure TConv1f1.Edit1Change(Sender: TObject);
begin

end;
(The Object Inspector will show you that there are 27 other events which arise from any edit box. Aren't you glad we don't have to think about them for a while? (27 with Lazarus, in June 2020. It was 14 in Delphi, long ago, when the first edition of this was written. Sigh.))

Put...
// of Edit1Change
... after the "end" shown above. Delete the "begin" just above that "end".

Copy what's in your buConvert OnClick handler, between (exclusive) it's "procedure..." line and the final "end", and paste it into the Edit1Change section of your code, making it...
procedure Tconv1f1.Edit1Change(Sender: TObject);
var singQuantity:single;
    sTmp:string;
begin
 try
  singQuantity:=StrToFloat(edit1.text);
  singQuantity:=singQuantity/0.3937;  //new line
  laOutput.caption:=FloatToStrF(singQuantity,ffFixed,7,2);
 except
  laOutput.caption:='Could not convert';
 end;//try.. except
end;// of Edit1Change
You need not only what's between the handler's "begin" and "end", but also the declaration of your local variables, singQuantity and sTmp.

Be careful you do not to add or remove "ends"... and watch out for "ends" being stuck in by Lazarus! Remove them too, if they arise.

In a moment you are going to try running the application again. When you do: DON'T, for now, type anything into the edit box that isn't a digit, i.e. 0,1,2,3...8,9. You can use the delete key, but DON'T, for now, delete everything in the box. Always leave at least one digit.

Okay- run it! The program should now be working without using the "Convert" button!

Quit the application, bring up the form, click on the "Convert" button, press your delete key... the button should disappear. Re-save your project. Re-run it, again putting only numbers into the edit box. It should still work, as long as you only enter numbers, and always keep at least one digit in the box. Quit the application again.

----

Long tiresome digression begins.

Do a backup of your work so far.

Look through the code. You will still find...
procedure TConv1f1.buConvertClick(Sender: TObject);
var sTmp:string;...
... even though there's no longer a Convert button to click!

This does no harm, other than wasting space and introducing something which might confuse you when looking at your code later. Always do what you can to eliminate opportunities for confusion! In this case, we will remove the redundant OnClick Handler.

Don't remove it the obvious way. That will leave troublesome loose ends.

Think back to the creation of the event handler. You double clicked on the button, and the IDE created the skeleton of the handler. Some parts of that skeleton were not obvious at the time. I didn't explain them, because they are not important to you yet. However, they are there, and need to be respected.

If you look at the early parts of Conv1u1, you'll see...

It starts with the word "unit"
A little farther down, you'll see "uses".
A little farther down, you'll see "type".

Within that section, you'll see "procedure buConvertClick(Sender: TObject);". Don't do anything to it. The IDE will deal with this part of the code for you.

Go down to where you have....
procedure TConv1f1.buConvertClick(Sender: TObject);
var sTmp:string;
    singQuantity:single;
begin
try
singQuantity:=StrToFloat(edit1.text);
singQuantity:=singQuantity/0.3937;
laOutput.caption:=FloatToStrF(singQuantity,ffFixed,7,2);
except
laOutput.caption:='Could not convert';
end;

end;
Make that just

procedure TConv1f1.buConvertClick(Sender: TObject);
begin
end;
... in other words take "the heart" out of the definition of buConvertClick. Be careful to leave in the "begin". Be careful to take out enough "end"s, without taking out too many. Take out the declaration of the local variables. There should be just one "end" left before....

procedure TConv1f1.Edit1Change(Sender: TObject);
.. or whatever you have after buConvertClick.

Now re-save the project. Under Delphi, the....

procedure TConv1f1.buConvertClick(Sender: TObject);
begin
end;
... "magically" disappears, along with the reference in the type clause at the top of the program. Be sure it still runs okay! The IDE does a good job of managing things, and you should rely on it.

Under Lazarus, you need to, by hand, delete the...

procedure TConv1f1.buConvertClick(Sender: TObject);
begin
end;
...and the "forward declaration"... which is just the single line...
    procedure buConvertClick(Sender: TObject);
... up at the top of the code, in the...
type
  { Tconv1f1 }
  Tconv1f1 = class(TForm)
... block at the top of the code.

Save what you've got, and run it again. It should still work!

Just in case, here's what the whole thing should look like at this stage....
unit conv1u1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type
  { Tconv1f1 }
  Tconv1f1 = class(TForm)
    Edit1: TEdit;
    laOutput: TLabel;
    procedure Edit1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private

  public

  end;

var
  conv1f1: Tconv1f1;

implementation

{$R *.lfm}

{ Tconv1f1 }

procedure Tconv1f1.Edit1Change(Sender: TObject);
var singQuantity:single;
    sTmp:string;
begin
 try
  singQuantity:=StrToFloat(edit1.text);
  singQuantity:=singQuantity/0.3937;  //new line
  laOutput.caption:=FloatToStrF(singQuantity,ffFixed,7,2);
 except
  laOutput.caption:='Could not convert';
 end;//try.. except
end;// of Edit1Change

procedure Tconv1f1.FormCreate(Sender: TObject);
begin
  top:=20;
  left:=60;
  Edit1.text:='999';
end;

end.


Long tiresome digression ends! (^_^)

A little way back, I said you should run the application, but that you should not enter anything non-numeric. In a moment, I want you to run it and put in something non-numeric, but first you should understand what you are going to see. As soon as you enter something non-numeric, the StrToFloat in the OnChange handler is going to have a problem. When it has that problem, the IDE will stop your program and put an error warning on the screen. Click the "continue" on the message dialog. You'll get a new error warning; click continue again.

You are seeing the error warning because you ran it from within the IDE (Lazarus or Delphi). To see what a customer would experience, stop the program, if it is running.

Use Windows Explorer/ File Explorer or your OS's equivalent to go to the exe file that the IDE created. Run that. Now the program "works" without interruption. When you enter non-numerics, the try... except...end structure does it's job (you can see the "Could not convert") message cropping up)... but, because the IDE is not supervising the execution of the program, you don't get the error warnings. Ta! Da!

We've done it!


Have a look at the "In this lesson...." material at the top of the page. Be sure you're clear on the various points.



Search just this site without using forms,
Or... again to search just this site, use...

Powered by FreeFind

Site search Web search

The search engine merely looks for the words you type, so....
  *!  Spell them properly   !*
  Don't bother with "How do I get rich?" That will merely return pages with "how", "do", "I", "get" and "rich".



I have other sites...
   SheepdogSoftware site.
   My site at Arunet.


Ad from page's editor: Yes.. I do enjoy compiling these things for you... I hope they are helpful. However.. they don't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows 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 Sheepdog Software freeware, shareware page.
Link to Tutorials main page Link to Lazarus/ Delphi Course index

To email this page's editor, Tom Boyd.... Editor's email address. Suggestions welcomed! Please cite "conv1.htm".

Click for W3.org HTML validity test Page has been tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org. Mostly passes.

AND passes... Click to check CSS validity


One final suggestion: Be sure you know all you need to about spyware.

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