This tutorial should expand your grasp of using subroutines- functions and procedures- in your programming. It will start with "just doing it"... but after a little bit, we move on to a bit of me talking, you, I hope, reading.
Would you expect to sit down in the cockpit of a modern jet fighter, and learn to fly it by "trying things"? No. I know that reading what teachers write can be dull... but it is their best shot at giving you understanding of the concepts behind something so that you can get it "flying" more easily!
The tutorial may look "long", but fear not... it only fills space 'cause your hand is held, and you're told almost every click you need to make. The ideas in it are straightforward... but important.
====
Some of the material in other Level One lessons is reiterated here. This is partly so you can start with this tutorial, and partly to reinforce what you may have learned already.
This tutorial was tested using version 2 of Delphi, on a Windows 98SE machine, but there shouldn't be significant difference for other circumstances. We are only doing fundamental things.
Even before you fire up your Delphi, create a folder for the work of this tutorial. Call it DD70. It can be anywhere, but I would suggest putting it in a folder called something like "Delphi Sourcecode", and putting that folder in your "My Documents" folder. There's no need to do more than is listed. For example, you are going to create a label, but you can leave the label with it's default name, "Label1". (Ordinarily, it is a good idea to give things meaningful names, but this is just a little application, and we don't need to fuss... this time.)
You can skip any of the following notes if you had no trouble doing what you were told to above.
Note 1: To put a label on the form, click on the icon for a label in the Component Palette (bar of icons across the top right of the screen. You need to be on the "Standard" (components) tab. Then click somewhere on the form.
Note 2: The Object Inspector is usually showing on the left of your screen. Press F11 to bring it up/ switch to it, if need be. Whatever component (the form itself, buttons, labels, etc) is selected on the form which is being designed will be the object being "inspected". You can change object just by clicking on something else on the form, or by making a selection from the listbox at the top of the Object Inspector. The object inspector has two tabs. For now, you want the Property tab. It has two columns. The first lists column all of the properties the component you are inspecting. The second column tells you, and in most cases allows you to change, the current value of each of those properties. Don't worry! Most of the time you can ignore most of the properties!
(End of "notes")
Moving forward....
Add a button to the form. Double click on the button, and you should find yourself looking at....
procedure TDD70f1.Button1Click(Sender: TObject); begin end; end.
put "close;" (without the quotes) between the "begin" and "end;".
Click File | Save All again. This time you aren't asked for the names for anything. As in most Windows programs, the system just assumes that you want to over-write the previous versions of the things you are working on. Not I said "things", and "Save All" Don't worry too much about what the "things" are... the system takes care of saving your code, the details of the form you've specified, and it's buttons, etc. There's an icon for "Save All"... a folder with a bent arrow pointing to a grey box. The tooltip will confirm you've got the right one. (There's also an icon for running the program- green triangle- or you can press F9.)
Run the program again; click the button. In some tutorials I recommend using "application.terminate" where I've told you to use close. I'm not entirely sure of the difference! Both work for the things I do.
Name the button buQuit, and make it's caption &Quit if you're feeling tidy-minded. Note that you can do this even after doing what we've already done. Originally, the button's name was Button1, and so the procedure we made was called Button1Click. As soon as the button was renamed, Delphi took care of renaming the procedure buQuitClick. Pretty cool!
Did you realize you've already put a procedure into this application?
Procedures (and the other sort of sub-routine, functions) help keep your code (instructions for how the application should behave) organized. Everything to do with what should happen when users click on buQuit has been given a place to be: In the "buQuitClick" procedure.
When you use Delphi to help you create procedures associated with buttons and such... and there are an awful lot of "and such" things... it creates a bunch of things that you won't want to know about yet. None- the- less, using Delphi thus is the way to go. In a moment, we're going to make a much simpler procedure, but we need the Delphi- created procedures to do almost anything.
Have a look though the rest of the sourcecode, the text that has.....
procedure TDD70f1.buQuitClick(Sender: TObject); begin close; end;
... in it. Near the top, you will find....
procedure buQuitClick(Sender: TObject);
It is nearly the same as the line before the "begin", but not quite the same.
Notice also that the sourcecode has the words "interface" and "implementation" in it. (Don't worry... we'll be done with "things to notice" in a moment!)
These words mark the start of two big sections of the sourcecode... the interface and implementation parts, happily enough.
Let's talk about the second section first. The details of how things are going to be done, of what must be done, are in the second section. Thus there are four lines just about the very simple job that must be done when buQuit is clicked.
In the first section, there is only one line about the buQuitClick procedure.
Let me define a word: Compiler. A compiler is some software that takes something that is somewhat human friendly, human readable. It's job is to take that and convert it into the numbers the computer needs to do a job... numbers that are very, very UN- human- friendly.
Now that you know what a compiler is, I can tell you that the purpose of the first section of the sourcecode is to provide the compiler with an outline of what's in the second section.
A bit of good news: Any procedure or function you add to your sourcecode will just need one line in the first section, and what is added to the second section will always follow the pattern you've already seen with the buQuitClick procedure. Of course, there will often be more that one line between the "begin" and the "end"... and there may be other things elsewhere, too... but the basic pattern will still be there.
An aside.... When I say "line", I mean a line as far as the compiler is concerned. Many times this "line" will only occupy one line on the screen. If you had a very wide screen, and worked with a small font, you could always have the sourcecode line occupy only one screen line... but there's no need for that. Adjust your browser so that the next line takes up most of one screen line. (If your browser is currently filling your screen, right- click in the title bar, and select "Restore", then drag one of the sides to where ever you need to get the next line to take up most of one screen line.
The quick brown fox jumps over the lazy subroutine.
That means the same thing written as above, or as....
The quick brown fox jumps over the lazy subroutine.
(End of "aside")
Put another label on the form, and two buttons. Name the label laWink and the buttons buWinker1 and buWinker2.
Double click on buWinker1, and the following should pop up....
procedure TDD70f1.buWinker1Click(Sender: TObject); begin end;
Between the begin and end, put....
if laWink.caption='[[321*--' then laWink.caption:='--*123]]' else laWink.caption:='[[321*--';
Do a "Save All", and try running the program. When you click on buWinker1, you should see laWink change. Click again, it changes again. Click it again and again, and you'll see laWink "winking".
Now then... we COULD just do almost the same thing with buWinker2... but we're not going to. It is rarely a good idea to have duplication within a program. And in any case, we want to see a simple, programmer created, subroutine.
Just before the final "end." of the sourcecode, insert the following. Don't try to run the application until you are told to. The stuff between the "begin" and "end" are the same as that which we put into buWinker1Click, so just use copy paste. (You could, of course, just copy paste all 6 lines directly from here!
procedure TDD70f1.Wink; begin if laWink.caption='[[321*--' then laWink.caption:='--*123]]' else laWink.caption:='[[321*--'; end;//Wink
Note that you've added an end to the sourcecode. The one marked "//Wink" is new, and in addition to the "end." which follows it.
The rest of a line after a "//" is ignored by the compiler. It is wise to litter your sourcecode with little notes to yourself like this. (The marked "end" is the end of the "Wink" procedure.
You haven't tried to run the program yet, I hope?
Up near the top of the sourcecode, you'll find....
public { Public declarations }
Just after these two lines, add....
procedure Wink;
(Don't forget the semicolon)
Double-click on buWinker2.
In the....
procedure TDD70f1.buWinker2Click(Sender: TObject); begin end;
... which results, add just....
Wink;
... between the "begin" and "end".
Now run the program! If you click on either of the Winker buttons, laWink should change.
So! It "works"... but we're not done yet!
In "procedure TDD70f1.buWinker1Click(Sender: TObject);" replace everything between the "begin" and "end" with just "Wink;"
(Yes... when you know more about assigning event handlers, you'll be able to do even more clever, efficient things. But we're learning to walk here... save running for later!)
Do you see what we've done? We've taken something... the winking... out of where it might be put, and "parceled it up" in it's own little corner of the sourcecode. There are many reasons... which I won't explore fully here... that doing such is a Good Idea. But just for starters, look at the other bits of your sourcecode. Can you see, from reading the sourcecode, what happens when you click on buWinker1 or buWinker2? Something "Winks". We may not know all the details... we may not WANT to know all of the details... but it HELPS MAKE YOUR SOURCECODE CLEAR if you use subroutines (procedures and functions... which you'll meet in a moment) to "parcel up things."
I'll show you another reason to use subroutines. Within the code for "Wink" change the "-*123" to "-*456789", and re-run the program. Notice what you've done? With ONE change to the program, you've altered the behavior of BOTH "Winker" buttons.
That's it! You have made a great start towards knowing about procedures!
You have, in essence, made up a "new word" for the Delphi language. It didn't have "Wink" as something it could do, previously. Not only have you added a word, but it is a word that you control. Whatever you put between the begin and end is what "Wink" does!
Onward to functions!
Don't try to run the program until you are told to. In the interface section, just after the line saying "procedure Wink;", add a new line....
function sPadIt:string;
Just before the "end." at the end of the sourcecode, add....
function TDD70f1.sPadIt:string; begin result:='XXXXX'+laWink.caption+'XXXXX'; end;//sPadIt
Add a label to the form; name it laFilledWithFunction.
Within the sourcecode for "Wink", just before the "end;//Wink", add...
laFilledWithFunction.caption:=sPadIt;
Now run the program!
You've made up another word! This word rather than "doing something" returns something. When the application runs, every time execution goes past an instance of "sPadIt", you "get" a string. The string consists of 5 X's+whatever's in laWink at that moment+ 5 more X's.
Note that word "result" in the sourcecode. When you program with functions, you define what the function does once, somewhere in the sourcecode. If you have done that, you can use the function anywhere else. We only used sPadIt once, but that is unusual. When we used sPadIt (inside (WinkIt), we put sPadIt in a place where it had to "boil down" to some string, so that string could be "put" "in" the field called laFilledWithFunction.caption. The word "result" is "special". It will only appear within the code defining how a function's value is generated. Whatever is in "result" at the end of the function's execution is what the application uses in place of the "call" of the function. What is in "result" is what the function "boils down to".
====================What we've done in this tutorial is not very exciting, or useful, perhaps... but a start towards more useful functions which I hope you will try to use in your programs. They are one of those things that until you use them, you won't see how useful they are.... Sigh. Did someone say programming was easy? Is riding a bike easy? Tell my 4 year old nephew it is. Tell my 10 year old niece that it isn't. You WILL be able to program... one day. And like how to ride a bike, how you go from "can't" to "can" is a bit of a mystery. You just have to keep plugging away at it. Struggle long enough, and, like the bike, suddenly you "get it".
We've only scratched the surface here... fear not, there's lots more to learn, and I will try to provide more tutorials. (In the meantime, there is useful material on these matters in my Pascal Tutorials). Various ways of passing things to and from subroutines , and issues of "scope" are of major importance. But you've made a good start on some essentials already.
____In some of my other, earlier, tutorials I've described adding subroutines to sourcecode in a variety of places. Each approach has pros and cons. At May 2007, I believe that adding them as described here is the right way to go, i.e.:
Put the forward reference, the part in the interface, in the "public declarations" section. That section is a subsection of the type declaration for your form. Right at the start of that in any sourcecode will be the equivalent of the line that in this demonstration reads.....
TDD70f1 = class(TForm)
The name of the type, "TDD70f1" in this instance, has to appear before a dot, before the procedure (or function) name when you give the details of the procedure (or function) in the implementation part, e.g., in this demonstration....
procedure TDD70f1.Wink;
.... BUT, as you will have noticed, if you have looked carefully, the "TDD70f1." part is absent in the already mentioned forward declaration, the entry in the "public declarations" section. This is because as the "public declarations" section is part of the declaration of "TDD70f1" itself, the compiler "knows" you're talking about a TDD70f1 procedure.
____A little word about the "TDD70f1".... that element is always made up of a "T" (for "type"), followed by form's name. (As in the name of the button, as we saw a long time ago, it is okay to change the name of the form even after you have made a start on an application. Delphi will take care of fixing all related entries, except entries in comments, where you are unlikely to need or want to make reference to the form's name.
____What to call a subroutine? Call it something that makes what it does clear. The name should start with a letter, and after that it can consist of letters and digits. You have to eschew words that Delphi is already using, so you can't, for instance, have a procedure called "Begin". You could have something like "BeginFinalPart", if you wanted to... but I'd avoid even using Delphi words that may jump out at you, like Begin. "To", on the other hand, is also a Delphi word, but I wouldn't hesitate to use it in names, e.g. CalcTotal, BringUpToDate, etc, etc.
Notice that a procedure or function's name cannot contain spaces. I prefer to use capital letters to make the parts of a compound word clear. Other people use underscores, e.g. Calc_Total, Bring_Up_To_Date. Not only are the underscores easy to overlook when text is highlighted, but they are a pain to type. You can't use hyphens instead of underscores. (I guess the poor old hyphen, already meaning "minus" and "negative" has been ruled out as something that's allowed in a name!)
A function always "returns" a value. When reading the sourcecode, where ever you see a function, you can say to yourself "That bit there, when the application runs, will "boil down", will be equivalent to, a value. Therefore, you will, once you become familiar with Delphi, realize that functions always have types. This is the same "type" that variables have. Functions (and variables) can be of type "string", as was the case of sPadIt. They can be of type byte, integer, boolean, and a host of others. As with variables it is a good idea... though not obligatory... to include a preface with every name to indicate the type of the function or variable. E.g. sPadIt was called that because it was a string variable. There are no "rules" for what you "must" use, but I tend to use "b" as the start of the name of a function that returns a byte, i for integer functions, bo for boolean functions. The first letter after the type designation should be a capital. You'll see why when you think about how confusing working with, say, bopen would be. Would that be a boolean function (or variable) concerning a pen, or a byte type function about, say, how many times something had been opened? boPen and bOpen would be less ambiguous. (This naming convention is called Hungarian notation, about which you can learn more in the Wikipedia entry.)
|
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 .....