This page is "browser friendly". Make your browser window as wide as you want it. The text will flow nicely for you. It is easier to read in a narrow window. With most browsers, pressing plus, minus or zero while the control key (ctrl) is held down will change the texts size. (Enlarge, reduce, restore to default, respectively.) (This is more fully explained, and there's another tip, at my Power Browsing page.)
In earlier tutorials, we have looked at some simple Lazarus programming. In this tutorial, we are going to look at the general "shape" of the code behind the applications. We are going to identify the parts of any Lazarus code.
As was true of the previous tutorials, what you read here is almost the same as what you would read if I wrote the same tutorial for Delphi learners. I'll try to note differences as we proceed. Please write to me if you find I've failed to note a Lazarus / Delphi discrepancy.
This is another of the tutorials which is rather dull... but it does contain some important stuff, and the more you can concentrate as you struggle through it, the easier your programming will be hereafter. If you think reading it is tedious, give a thought to how tedious writing it was!
You may have seen the following code before. It is okay if you haven't. If you are very new to Lazarus, you might want to work through the tutorial which led to this code, but you probably do not need to for the points I want to make in this tutorial.
unit ld002u1; (*A very simple arithmetic testing program for helping users master 2+3, 8+2, etc. Written as an example of using Lazarus or Delphi, and explained at.... https://sheepdogguides.com/lut/lt1Na.htm *) {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls; type { TLD002f1 } TLD002f1 = class(TForm) buQuit: TButton; eAnswer: TEdit; laQuestion: TLabel; procedure buQuitClick(Sender: TObject); procedure eAnswerChange(Sender: TObject); procedure FormCreate(Sender: TObject); private { private declarations } bNum0,bNum1:byte; sAnswer:string; procedure PrepareQuestion; public { public declarations } end; var LD002f1: TLD002f1; implementation {$R *.lfm} { TLD002f1 } procedure TLD002f1.buQuitClick(Sender: TObject); begin close; end; procedure TLD002f1.eAnswerChange(Sender: TObject); begin if eAnswer.text=sAnswer then begin showmessage('Right!'); PrepareQuestion; end; end; procedure TLD002f1.FormCreate(Sender: TObject); begin randomize; PrepareQuestion; end; procedure TLD002f1.PrepareQuestion; begin bNum0:=random(10); bNum1:=2; laQuestion.caption:='What is '+inttostr(bNum0)+ ' + '+inttostr(bNum1)+'?'; sAnswer:=inttostr(bNum0+bNum1); eAnswer.text:=''; end; end.
While that might be a little intimidating, all of that can be "boiled down" to just....
unit NAME;
STUFF
interface
STUFF
implementation
STUFF
end.
That's the basic outline of any Lazarus (or Delphi) unit.
It starts with the word "unit", followed by the unit's name, followed by a semicolon
I'm not going to say "followed by a semicolon" over and over again.
Where you see a semicolon, it is important. Where there is no semicolon, don't put one in "just in case". We'll talk more about semicolons as we go along, and in subsequent tutorials. You'll "get it" eventually, but if it takes a while, you won't be the first person to curse them.
After the "unit ld002u1;" line, which "sets the scene, tells the compiler what sort of things follow, you may or may not have some "stuff"
Eventually, you will (in a correctly constituted unit file) have the word...
interface
... which marks the beginning of the first section of the unit.
That will be followed by some "stuff".
Next you will come eventually to a line with the word...
implementation
... which will be followed by some more "stuff".
And, at the very end, you need to have...
end.
... and don't remove the "." after the "end"... it is required.
Happily, all of the above is created for you by your RAD ("rapid application development") environment, be it Lazarus or Delphi.
That's the overall structure of any unit.
Now we will delve into the things I called "stuff" in my abbreviated list of what is in a unit.
Both Lazarus and Delphi will do most of the work for you in regards to what goes before "interface".
That weird line....
{$mode objfpc}{$H+}
... Looks at first glance like two squiggly bracket comments, but the $ after each of the {'s makes each of them a special kind of comment called a compiler directive. The good news is that Lazarus will create what you need. Just don't "mess with it" until you know what you are doing. (Delphi doesn't create a compiler directive here, but it does create at least one in other places. Again: Just go with the flow.)
It is, however, a good idea to put lots of comments in this section. You should write a description in this section saying what the application does.
It is also a good place to put comments about current problems with the code. Also your "to do list" (features you are thinking of adding). A history of the application's development is also a good thing to put here.
If you follow my guidance, you will not put information about what version of the code this is in this section.
I hope you think that it is "so far, so good"? Pretty easy going, so far?
We're going to skip over the interface section for now, and look at "the heart" of the code, the implementation section.
Just to save you scrolling back here it is again...
implementation {$R *.lfm} { TLD002f1 } procedure TLD002f1.buQuitClick(Sender: TObject); begin close; end; procedure TLD002f1.eAnswerChange(Sender: TObject); begin if eAnswer.text=sAnswer then begin showmessage('Right!'); PrepareQuestion; end; end; procedure TLD002f1.FormCreate(Sender: TObject); begin randomize; PrepareQuestion; end; procedure TLD002f1.PrepareQuestion; begin bNum0:=random(10); bNum1:=2; laQuestion.caption:='What is '+inttostr(bNum0)+ ' + '+inttostr(bNum1)+'?'; sAnswer:=inttostr(bNum0+bNum1); eAnswer.text:=''; end;
The first line...
{$R *.lfm}
... is one of those compiler directives I told you about in the "Comments" tutorial. You don't need to worry about what it does. You don't have to remember to put it in. You do have to leave it alone after Lazarus puts it in for you.
The next line.....
{ TLD002f1 }
....is a simple comment, created by Lazarus from the name of the form, "LD002f1". (Don't worry about the "T" part yet!) This is the code that "operates" the form LD002f1, which is a form of the application LD002. (You will probably only use one form per application for a while, but an application can have multiple forms.) The "tidy" naming is all down to the programmer's choices, and the choices made here follow my working practices.
Then there are three...
I've gone a little overboard celebrating the word "blocks", but they are a great feature of Pascal, Lazarus, Delphi, HTML, and many other computer control mechanisms. They extend beyond the world of computers into anything engineers have had something to do with.
In broad terms, not exactly Lazarus terms... yet... a "block" is a discrete entity you can work with without worrying too much about the other blocks in the system. You might use the word "module" for the same concept.
In a car, for instance, there's the engine, the wheels and suspension, the passenger cell (keep you safe and dry), the lights (headlights, turn signals, etc).
Each of these are like the "blocks" I am trying to get you excited about. And part of the reason for being excited is that, in a well designed system, there can be blocks within blocks within blocks. And whatever skills you need to work with a block at one level also help you when you work with a block of another level.
Here is the first of the blocks in the code we are working though...
procedure TLD002f1.buQuitClick(Sender: TObject); begin close; end;
... and this is the next...
procedure TLD002f1.eAnswerChange(Sender: TObject); begin if eAnswer.text=sAnswer then begin showmessage('Right!'); PrepareQuestion; end; end;
... and this is the third block...
procedure TLD002f1.FormCreate(Sender: TObject); begin randomize; PrepareQuestion; end;
... and all that is left is a fourth block...
procedure TLD002f1.PrepareQuestion; begin bNum0:=random(10); bNum1:=2; laQuestion.caption:='What is '+inttostr(bNum0)+ ' + '+inttostr(bNum1)+'?'; sAnswer:=inttostr(bNum0+bNum1); eAnswer.text:=''; end;
Neat? You've already spotted, I hope, the similarities between those, but don't worry if you haven't.
A quick little thing to dispense with: It doesn't matter which order those blocks appear in. In fact, if you are careful, and take a whole block, and don't move it into the middle of another block, you can re-arrange the sequence of the blocks. I sometimes... not very often... do that to get several things that I am currently working on near one another in the code.
One of the great joys of working with blocks is that if you do it properly, you only need to look at one block at a time. Your work is made manageable.
To jump ahead a bit... not only can things be broken into blocks, as we are seeing at the moment, but you can assemble "little" blocks, and from them make bigger blocks, and from a few "bigger" blocks make a huge block... and at any given level, the details of how the smaller blocks are made are not "underfoot". But! To go back to what we were saying....
Each of those blocks boils down to the following. Where I've done something all in capitals, it represents a "placeholder" for a place where different things exist in different blocks...
procedure NAMEofPROCEDURE; begin STUFF end;
The NAMEofPROCEDURE thing will be taken care of as we go along in the various tutorials. Sometimes, for instance when you start to build an OnClick event handler for a button, the system takes care of everything for you. Other times you will have to make up a name, and properly "plug" the whole thing into your code. There are a few ways to go wrong doing the plugging in, but I will help you, as the need arises.
After the first line, "procedure...", in our examples, our blocks always start with begin, and end with end;. Don't miss the semicolon!
That semicolon is the "glue" between the blocks.
There will sometimes be certain things before the begin... you'll be seeing them as the need for them arises.
Let's look at the bodies of the four procedures...
= 1 ====== buQuitClick
close;
= 2 ====== eAnswerChange...
if eAnswer.text=sAnswer then begin showmessage('Right!'); PrepareQuestion; end;
= 3 ====== FormCreate(Sender: TObject);
randomize; PrepareQuestion;
= 4 ====== PrepareQuestion;
bNum0:=random(10); bNum1:=2; laQuestion.caption:='What is '+inttostr(bNum0)+ ' + '+inttostr(bNum1)+'?'; sAnswer:=inttostr(bNum0+bNum1); eAnswer.text:='';
The first procedure, buQuitClick, is a really simple procedure: It only does one thing!
The third (we'll come back to the second one) procedure, FormCreate, does two things.
See the semicolon on the end of "randomize"? It acts as "glue".... between a begin and an end, you are allowed, strictly speaking, one "thing"... more properly called a statement. This is what we have with the buQuitClick code.
But there's a trick. If you have two statements, e.g. "randomize" and "PrepareQuestion", and put a semicolon between them, the result...
randomize;PrepareQuestion
... "counts as" "one" statement, so it can go between a begin and an end! Such "out of many, one" statements are called compound statements.
And you are allowed to, advised to, stick in a carriage return to split things up on the screen to make them more readable....
randomize; PrepareQuestion
Now then. Another semicolon. Put one after the "PrepareQuestion", before the "end". you don't actually need one here. As long as between the begin and the end you have "a statement", be that....
close
(a simple statement), or...
randomize;PrepareQuestion
a compound statement, with semicolon "glue" between the constituent parts, all will be well.
But take it from me: Put in the semicolon after the statement, before the end. And extra semicolon, here, will do no harm. And if you always put it in, you won't have to deal with the problems which arise if you subsequently add statements to what is between the begin and the end, and you forget the "flue" semicolon.
Just to show you again, in full, the two we've been talking about...
procedure TLD002f1.buQuitClick(Sender: TObject); begin close; end; procedure TLD002f1.FormCreate(Sender: TObject); begin randomize; PrepareQuestion; end;
Are you beginning to see the method in the madness? I hope so. We're going to go of on a tangent....
I once taught a computer to "speak" English.
I said: Here are some nouns, suitable as objects...
... and here are some nouns for people...
... here are some adjectives...
... and some verbs...
Now then computer, I then said: Here's how you make a sentence....
Person-noun+ verb+ "the"+ (sometimes: an adjective)+ object-noun.
And immediately, the computer could produce...
Of course, things like "Henry sold the red river" came out... improbably, probably... but still intelligible.
Once you finish the hard work of some basic rules, it is often easy to expand them, if they were designed right. With Lazarus, the rules are "finished", and extensive... and as you progress, you will struggle to get started, but then things move ahead more easily. To extend the "language" example just a little further....
Conjunctions...
New rule: Here's another way to make a sentence....
Person-noun+ conjunction+ person-noun+ verb+ the"+ (sometimes: an adjective)+ object-noun.
And immediately, the computer could also produce...
Why did I introduce this tangent?
Programming is like the above. There is a certain vocabulary to master. And then the words you know have to be put together, but not "just anyhow". You have to follow very simple rules.
Learn the rules, and half of your programming hassles will go away.
One of the rules is that the code for an application, as I said at the start of this, consists of...
unit NAME;
STUFF
interface
STUFF
implementation
STUFF
end.
It might not always seem that simple... but it is!
Now all you have to do is learn what's allowed for the different sorts of "stuff".
We were talking about the implementation section, and we're nearly done with that.
Before we leave it, and cover the "interface" section, just a preliminary word about something which should have been bothering you.
Why do some "procedure..." lines look relatively simple, e.g....
procedure TLD002f1.PrepareQuestion;
... while others, e.g....
procedure TLD002f1.buQuitClick(Sender: TObject);
... are "fancier"?
The "(Sender:TObject)" stuff will be present, in that form, on the end of the procedure statement for event handlers created for you by double-clicking on something on your form. You don't need to know more than that for now. You don't want to put anything like that in your procedure statements, i.e. the lines that explain what a procedure created by you, like the "PrepareQuestion" procedure.
Both of those lines start "TLD002f1."
And they should. The "T" stands for "Type", and I've been wimping out, not addressing that, for some time now. (And will continue to do so).
The "LD002f1" part derives from the fact that we named the form we are working with "LD002f1" (for Lazarus Demo, number 2, Form 1). In the implementation part of your code, as long as you are doing things "my way", the procedure statements will always start with T<something-determined-by-the-form's-name>. and then the procedure's name.
We're now going to look more closely at...
procedure TLD002f1.eAnswerChange(Sender: TObject); begin if eAnswer.text=sAnswer then begin showmessage('Right!'); PrepareQuestion; end; end;
It boils down to...
procedure TLD002f1.eAnswerChange(Sender: TObject); begin if eAnswer.text=sAnswer then...
SOMETHING
end;
The basic if statement consists of...
if CONDITION then STATEMENT
The condition is something that will boil down to "true" or "false". In this case, either what is in eAnswer.text is the same as, is equal (=) to what is in the variable sAnswer, or it isn't.
The statement is any of the many, many, legal Lazarus statements, e.g. showmessage('Hi').
That's the simple case. But if you want to do something more complex, as we do here, you can "wrap" a bunch of statements together, "gluing" them with semi-colons, and "wrapping them" in a begin and an end.
You can't JUST "glue" them together with semi-colons, or only the first (basic) statement after the then will execute when the condition is true.
Here's another thing.
I hope you've done the tutorial in which the code was developed? The user defined procedure "PrepareQuestion" is itself a block of code, a mechanism for "packaging" details, to get them out of sight, out of the way of seeing the forest for the trees.
At one level, we don't care what happens within "PrepareQuestion". We just call that procedure, once during the FormCreate handler, and whenever a user gets a problem right. In the code dealing with those two times when a new problem is needed, we see just "PrepareQuestion". If we need (or want) to see more details, we go to the code that defines "PrepareQuestion". We are dividing the problems, and that should help us conquer the task of writing the application.
Well, I've put it off as long as I can.
When you start building a new Lazarus application with "File | New | New application", quite a bit of code is put in the Source Editor before you even add a "Quit" button to the application.
Much of this code is in the interface section, and, thankfully, much of this code you don't need to worry about... but I'll take you through it, in a moment. Some of what you see won't be exactly what is in the interface section of LD002, because as we worked with LD002, in addition to the bits we changed by hand, Lazarus altered things for us.
The following is a section of what is in my Source Editor after I've started a new application and added a button to the form. I haven't done any of my normal re-naming of elements of the application yet. You can see that the section I've listed starts at the word "interface", the start of the interface section, and goes down to the first thing after that section, the word "implementation", which ends the "interface" section, and at the same time starts the next section, the interface section, which we just finished discussing.
interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls; type { TForm1 } TForm1 = class(TForm) Button1: TButton; private { private declarations } public { public declarations } end; var Form1: TForm1; implementation
At this point, there are three things in the interface section:
Those are the sections, the "blocks" (although I stretch the term there) of our basic interface code block. Let's go through them...
For now, you don't have to worry about what is listed after uses. The list tells the compiler that for some of the things we want in the code, the compiler needs to look in some units, e.g. "Classes, SysUtils, FileUtil..." which are provided with Lazarus, and contain the details of how to do certain things. In due course, you may be writing units of your own, and using them in other programs via the uses statement at the top of those other programs. But you won't need those skills for a long time.
The next statement is...
type { TForm1 } TForm1 = class(TForm) Button1: TButton; private { private declarations } public { public declarations } end;
In earlier tutorials, we've talked about data types. So far, I have funked getting into the details of what an "object" is, and it limits how accurately I can talk about what is going on above, so forgive a "fuzzy" explanation?
The central essence of our application is a "thing" that Windows or Linux can work with, a "thing" the operating system can work with to put stuff on the screen in front of us.
Getting our application built proceeds in stages. First we define a new type of object. (The word "type" is used there both in the Lazarus sense, and in the everyday sense.) The "type" (in the Lazarus sense) that we have created, in the above, is named "TForm1". (The "T" comes from "type".)
Once we've created the TForm1 type, and I'm going to come back to that, we are able to do the last thing in the minimal interface shell shown above...
var Form1: TForm1;
That statement says, "You know the type called TForm1? I need an instance of that type, and I want to call it Form1."
The word var is built into Lazarus. It says that the "declaration" of something is coming. We are about to declare that we need a variable (or similar) created, so that we can subsequently use it.
In this instance, it is not quite a "variable" we are creating. We are creating something bigger. So big, in fact, that it pretty well contains the whole application.
Keep reading, but console yourself with the fact that you don't have to fully understand all of this now in order to write Lazarus applications... but the more you can grasp, the fewer confusions you will have elsewhere.
A moment ago, I said that "var Form1: TForm1" creates an instance of a TForm1-type thing.
"Car" is a concept not too far removed from the idea of a "type". My car, an old Subaru, is and instance of the general concept, the type, "car".
We've seen var before in earlier tutorials, in things like....
var bNum0,bNum1:byte;
In that line, we are telling the compiler that we need two variables. We are declaring them. That makes the compiler get ready for us to use them. We have declared that we need two variables, bNum0 and bNum1, and that they should be set up ready to hold byte-type data.
When we used var a moment ago with var "Form1: TForm1", we weren't doing anything radically different. It is just harder to get your mind around what a "TForm1" "thing" is. I haven't put "TForm1" in bold, because that is not a fundamental, built in, Lazarus word. We've created that type, rather like we created the user-defined procedure.
Which brings me nicely(?) back to further discussion of the type statement we looked at previously.
Strip out all the comments in it, and you are left with...
type TForm1 = class(TForm) Button1: TButton; private public end
The "end" here is partner to the word "type". The end marks the end of the type statement. The semicolon I didn't copy, the one after the end is not part of the statement, is merely "glue" to join this statement to the next in the overall structure of the application's code.
The first thing after the word type at the start of the statement is
TForm1=
"TForm1" is a name made up for the type we are created by sticking a "T" in front of the name of the form we are working with. The Lazarus system does a great job, by the way, of "back referencing" changes. An hour into working on an application, I can decide to change the name of the main form... and all the places which need changing are changed as soon as I merely change the form's name, using the Object Inspector. But for this to work, you do have to make the name change with the Object Inspector.
This is probably the place to repeat: Until you know more, resist the temptation to "fiddle" with things you haven't been shown about fiddling with.
So... we know what "type TForm1=" is about. Moving on...
type TForm1 = class(TForm)
Class is a build in Lazarus word, as is TForm. We won't go into detail here. Suffice, I hope, it to say that what you see above translates roughly to "Things of the TForm1 type will be things that we can do a lot with, as defined by the "TForm" definition.
Moving swiftly and ruefully on...
By having...
Button1: TButton;
.. in the first section of the type definition, we are saying that a button called Button1 is part of the "thing" that we will create in a moment with "var Form1: TForm1;"
(Lazarus took care of adding this line to our code when we used the human friendly, GUI, techniques to put the graphical depiction of a button on the form on our screen. TButton is a built in Lazarus word. It defines what a button is, has, can do.)
Let me remind you: We added (just) a button to the basic "empty" application before we stated working though what is in the initial code.
So! After the multi-line, complex, "TForm1=... bit", we come to two words:
private public
No semicolons, by the way. The two words private and public are section headings within the interface section, just as interface and implementation are sections of the application's overall code. As such, they don't need semicolons, because the compiler already knows the "shape" of things.
You will put various things into the code, usually between the words "private" and "public". Make your additions there until further notice, anyway. Where you put the things I'm going to talk about won't be important to a beginner's needs.
Put a semicolon after each thing you add after the "private"
What did we add to the LD002 while we were working on it? By the time we were through, the private section held...
bNum0,bNum1:byte; sAnswer:string; procedure PrepareQuestion;
The first two statements create variables which we will use later. The last says that we are going to have a user-defined procedure in our application. We "declare" the procedure here, we say what it will do later, in the implementation part of the application's code.
We've talked about this before, but now you are ready to understand something which may have puzzled you until now. But before I address that, a little detail to be clear about
In the private section you may have no, one, or many variable declarations. It doesn't matter what order you put them in... but put all of the variable declarations in before you put the first procedure declaration in.
A procedure is something you've met, one example of a subroutine. Another sort of subroutine, which you've barely met, is the function. You've met built in functions like random(). If you are doing my tutorials in sequence, you have not yet met user-defined functions... but you will before long. They aren't hard. They are useful.
As I said: After private, list all the variable declarations you need first. After that, the sub-routine declarations come... all of the declarations of user-defined procedures and functions. You can mix them up. Just be sure they come after the variable declarations.
(The same rule should be followed if you put anything in the public section.)
Whew!
Something you may be wondering about: When to "var", when not to "var". When we wanted to declare that we needed a TLD002F1-type "thing", we said....
var LD002f1: TLD002f1
... but when we declared "bNum0:byte", and "procedure PrepareAnswer", we didn't use var. The reason is context. You could say that the "var" was implicit in the "private" which came just before where we declared bNum0, procedure PrepareQuestion, etc.
It took me years to get to the bottom of when you have the TLD002F1. in front of the name of a user-defined procedure, because I was doing things in multiple, confused ways. It is actually not complicated at all. Do remember that it won't always be "TLD002F1"... but, if you use my conventions, it will be something like that.
Up within the...
type { TLD002f1 } TLD002f1 = class(TForm)...
... part, when you get down into the private section, and you are declaring, say, PrepareQuestion, you do NOT prefix it with "TLD002f1.", because, from the context, it is already implied that we are talking about an element of TLD002f1.
However, down in the implementation section, were we are setting out the details of what should happen when the "PrepareQuestion" procedure is invoked, then we DO include the TLD002f1. to say "the PrepareQuestion which is part of the TLD002f1" "thing". Thus...
procedure TLD002f1.PrepareQuestion; begin bNum0:=random(10); bNum1:=2; laQuestion.caption:='What is '+inttostr(bNum0)+ ' + '+inttostr(bNum1)+'?'; sAnswer:=inttostr(bNum0+bNum1); eAnswer.text:=''; end;
You must be tired by now, but this is the time to mention something further.
In another tutorial, at the bottom, I advocated having a constant called "ver" in every application you write, and putting a string in that which identifies the version of the particular .exe file which a user has.
The Lazarus "skeleton" we have struggled through in the hundreds of lines in this tutorial does not illustrate every possible ingredient of a Lazarus application.
The const statement lets you create, funnily enough, CONSTants!
To create a constant called "ver" (for version), and fill it with "1 August 2011", all you need is....
const ver='1 August 11'
(You follow it with the usual semicolon, to glue this statement to the next in the application's code.)... and you put your const statement just after the uses statement, near the top of the code.
I expect that took significant willpower to plow through. I promise you that I really do think that a grasp of what I was trying to convey is important. I hope you will revisit this tutorial when you have more experience, see if some of the things that didn't make sense this time make better sense when you are more Lazarus experienced.
In my manic frenzy to tell you "everything", it is hard for me to be as selective as perhaps I ought to be, but there is one thing I believe you can safely "neglect".
If you followed my advice, and created a folder for each of the Lazarus (or Delphi) applications you have created so far, you may be puzzled by all the files which somehow accumulate in those folders.
The .exe file is the only file you need to pass on to anyone you want to give your application to.
The .lpi file is the one you should double-click on, if you want to launch Lazarus with that project loaded for modification.
Apart from those two, you don't need to know what the other files are all about!
If you want to move your work on an application from one system to another, just send all of the files in the folder across.
|
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.
Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org
....... P a g e . . . E n d s .....