Programming Using NSBasicCE
ARTICLE 6--CHECKBOX AND VARIABLES
All content (c)1999, 2002 Serg Koren.
All rights reserved.
Welcome to the sixth installment of NSBasicCE.
This time we go over another user interface widget, the CHECKBOX. We also finally talk about variables, what they are and how to use them.
Leftovers from Last Time
Last time we talked about MSGBOXes and I asked you to explain what the following nested call does:
MSGBOX MSGBOX("My homework!",vbYesNoCancel,"Homework "),vbOKOnly,"My MSGBOX homework"
a) How many functions are there?
b) How many subroutines are there?
c) What gets called/executed first, the subroutine, or the function?
d) When you run this line, what is that number that pops up? Run it again and try something else.
e) How many parameters does this line have?
The answers are:
a-b) Actually these are trick questions. There are no subroutine and functions in the line. There are however calls to subroutines and functions.
- 1. Remember that functions return values and use parentheses around their parameters in the call. So the function call is:
MSGBOX("My homework!",vbYesNoCancel,"Homework") and it returns its value to the outer MSGBOX call:
MSGBOX <…>,vbOKOnly,"My MSGBOX homework."
- 1. Remember that a subroutine call does not use parentheses around their parameters and doesn't return a value. So the subroutine call is:
MSGBOX <….>,vbOKOnly,"My MSGBOX homework."
- The function. Typically things get executed first line to last line. Each line gets executed left to right. However nested calls (and as we shall see below variable assignments) get executed differently. Nested calls get executed from the innermost call (the function in this case) outward.
- The number that pops up is the value of the inner MSGBOX function call. The outer MSGBOX call displays it. Specifically, the number represents the value of the button that was clicked in the inner (function) MSGBOX call. You can validate this by looking up the number you get in the manual.
- This one depends on your point of view. You could say: 3 or 5 Basically anything separated by a comma counts as a parameter. You get the answer 3 if you consider that for the outer MSGBOX (subroutine call) the first parameter is the entire inner MSGBOX (function) call and that counts as a single parameter.
One thing which you probably discovered (and I left for you to figure out on your own) about the MSGBOX is that it's buggy. That is, if you use the MSGBOX on an HPC it works fine. However, the MSGBOX doesn't fit on the screen properly i you use it on the CasioE-105 and other PPCs. There is no way to resize the MSGBOX on the screen so that it fits. This is a Microsoft bug. The only way to use a MSGBOX on the PPCs is to write your own from scratch. I've saved you the trouble, and if you need a MSGBOX on a Casio-E105, you can use mine, which you can find at: http://www.VisualNewt.com/CE/NSB. Please note that using my MSGBOX is a bit different from the standard one. This is due to the limitations of WindowsCE and NSBasic, so make sure you read the documentation and examine the sample file on how to use my version. On to new stuff!
Variables
We've used variables in some of our example programs, and articles, but I've never really explained them. I think it's time I do so, since we'll be using them a lot from now on, and if you're a beginner to programming, an explanation is in order. If you're familiar with variables you can skip this section.
"Variables" is a somewhat misleading name. From the name you would think that variables vary something. They do, but more importantly, variables store and hold things. What things depends on the type of variable, but generally speaking variables can hold two types of things: numbers, addresses. In programming, just like in school, there are all sorts of types of numbers: integers, floating point, imaginary, etc. In most programming languages, you have to worry about what type of number gets stored in what type of variable.
That is, some types of variables can only store integers, while other types can only store floating point, etc. BASIC (and NSBasic) is more flexible; they allow you to not have to worry about the type of variable and the type of number that is stored in it. This sounds like a great idea, and it is, but as in real life there are always tradeoffs. This ease of use gives up a little in the efficiency (how fast) your program runs, and how much storage (memory) the variable uses. And really, NSBasic allows you to get the actual type and deal with it if you need to do so. But most of the time, you don't have to worry about either the type or the efficiency of your program. The other major type that a variable can store is an address. Address?
Think of your WindowsCE's memory as being a huge ruler marked in inches. The ruler is laid out on the street and goes from 0 all the way up to 8Meg (about 8 million) inches (your device may have more or less memory). Now also assume you can store exactly one object at each inch. You can't store stuff at 1.5 and you can't store 3 things at 2 inches. One object and only at an inch marker. So if I had stored a pink poodle somewhere on the ruler (albeit a tiny one), how would I tell you where to find it? The easiest way would be to tell you the inch marker the poodle was sitting on. That would be the "address" of the poodle (2105 is where I had put the poodle.) So an address is just another type of number. So your memory ruler stores numbers. One per inch marker (address) and only at exact markings. Ok so what type of addresses can NSBasic handle. Obviously not pink poodle addresses.
If you followed the above you've probably surmised that addresses must have other numbers stored at their locations. So an address can have a number. An address can have another address. So what about objects? Objects are really just represented by a whole slew of numbers (depending on the object). But we can only store one number per location, so what d we do? Well the simplest thing to do would be to take our object (made up of a lot ff numbers) and store all the numbers at sequential locations on our ruler (making sure first that there aren't any other objects in the range we need). Ok, so we've laid out our object in sequential spots on our ruler (I'll start calling our ruler "memory" now, since I thing you get the point).
We can tell our program where our object is in memory by pointing to the start of the sequence of numbers we've stored. This starting point is known as a "base address". One other piece we need to know is the end of the object, but we don't store the end of the object usually, we store the number of numbers in the object (its length). So given the base address of an object and its length (which we store in another location in memory) we can get our object. What happens if we can't find enough sequential spots for our object? We run out of memory and get an error message (or crash usually in the MS world)!
Phew! Most of this is just background info, and you really don't need to know it for programming NSBasic, but it doesn't hurt to know. In the last article we also mentioned briefly there were two types of parameters. Now we can talk about them. There are parameters which get "passed" by value. The one's we've talked about, mostly. And ones that get passed by something known as "reference". "Reference" is just another piece of computer-jargon, which means, "address". So, if a parameter is passed by reference, we don't actually pass a number, we pass the address of the number. Why do we care? Well each parameter can only hold one number, just like our memory ruler. So if we need to pass something like an object to a subroutine or function, we do so by passing its base address by reference (NSBasic knows how to figure out the length if it knows where the base address is). Another type that gets passed by reference is an array, which we'll discuss next time.
Take a break, drink some orange juice, and absorb the above.
One thing I should explain (which has nothing to do with programming, but I've run across this innumerable times), is the difference between memory, storage, RAM, ROM and hard disk space. Amazing how many people confuse these. Briefly! Memory is RAM. It's the memory used when your program runs. Storage is a generic term for where things are held when you power off the computer. This is usually hard disk space, ROM, or something called Flash RAM. RAM stands for Random Access Memory (so it's not storage, unless its "flashable"). ROM stands for Read Only Memory, and you can't write to it, so its only use is for storage. You can't run programs in ROM. Hard disk space is used only for storage. Nothing ever runs on a hard disk. Things only run in RAM. So, if someone asks you how much memory you have you should tell him/her how much RAM you have in your system (8M, 32M, 128M etc.) If they ask you how much storage (space) yo have in your system you tell them the size of your hard drive usually (100M, 2G, EEC.). Believe me this little bit will help any customer service techs you may deal with from getting frustrated, pulling their hair out, and shooting their co-employees.
Ok, now that I've gotten that out of my system (no I'm not a customer service tech), and we've described what variables do, how do we actually use them in a program? Well it's all well and good that we can use an address in memory, but it's not easy to remember that spot 3198 has the location of the number 3 stored in it, so we give our memory slots a name. What name is up to you in your program. You make up a name, and NSBasic (or whatever language you use) will decide to store anything you use with that name at a given spot in memory. So you don't even have to worry about where in memory get stored (usually) just the name you gave it. So this should tell you that you should alwaysgive your variables meaningful names. Names you can understand and that describe what the variable holds.
You can either "declare" the variable first (to declare something in programming, means to tell NSBasic that you will use this name to store something), or you can just use the variable to store something. Why would you want to declare something? You should always declare variables before you use them. This not only serves as documentation (making it clear you're going to be using this set of names as variables) but it makes your program run a bit quicker because the program can find all the space it needs to store your variables before you use them, instead of hunting for memory as it runs. It also lets you know you've run out of memory while you're still writing your program before someone runs the program you just gave them. So how do you declare, or tell the program that you intend to use a variable? You use a DIM statement. DIM is short for DIMension. DIM used to be only used to declare arrays of numbers and arrays have something known as dimensions. In old-style BASICs you had to declare or DIMension an array, but you didn't have to (actually couldn't) dimension a single number. Newer BASICs (including NSBasic) allow you to dimension both. If you look up DIM in your manual you see it looks confusing. We'll ignore the confusing bits for now, and rewrite the statement as:
DIM nameA, nameB, nameC…
So lets say we want to get the circumference of our pool if we know its radius. The formula you may remember is:
Pi * Diameter = Circumference
We would need to have to declare four variables:
Pi, Radius, Diameter and Circumference
The easiest thing to do is just use those names, so in our program we would have the lines:
DIM Pi, Radius, Diameter, Circumference
(or we can have one per line or other combinations such as:
DIM Pi, Radius
DIM Diameter, Circumference
…these are all equivalent)
Now we've told our program we're going to use these variables to store numbers. How do we actually store a number into each of these? We use an equal sign:
Pi = 3.14 ' the variable name is always the leftmost thing in the line…
Radius = 100 ' for an assignment statement.
An assignment statement (more computer jargon) just assigns something to a variable name. Now all we have to do is calculate our formula and assign its result to our Circumference variable.
Diameter = 2 * Radius ' note that the variable name that we are assigning to is on the left side again
Circumference = Pi * Diameter ' same here
From the above you can see that:
2 * Radius = Diameter
is not a valid programming command, because the variable we're trying to assign in to is on the right, instead of the left. NSBasic would try to take the value stored in Diameter and store it into whatever (2 * Radius) was, but (2 * Radius) isn't a valid variable name, so NSBasic gives you an error. Basically this means that NSBasic won't let you store a number into another number:
3 = 2
but only into variables:
MySpot = 2
Even though MySpot is actually an address of a memory spot (a number).
Another confusing thing for programming newbies is that they may be confused to lines similar to:
Diameter = Diameter + 50
Algebraically, this is of course nonsense in most cases. But this isn't algebra. Although the equal sign (=) is used, it doesn't really read as "Diameter Equals Diameter Plus 50". Instead, it should be read as "Stored into" and read from the part to the right of the equal sign: "The value stored in the variable Diameter plus 50 is 'stored into' the variable Diameter." What this does is increment the value of the variable called Diameter by 50. So if the value of the variable (I'll just say 'variable' from now on) Diameter was 20 before this line gets executed, it will be 70 afterwards.
One last thing about variables: something called scope. No, this isn't a mouthwash. Scope is more computer-jargon (sorry, but it comes with the territory). Scope is easy to grasp, but hard to explain. Think of scope as a set of brick walls (or more accurately solid fences). The fences enclose other fence enclose other fences, etc. Inside each fence there are a number of cows. If you're outside the outermost fence you can't see anything inside. If you're inside the outermost fence you can only see the cows in your fence ring. As you go deeper into the fences you learn more and more about the number of cows in each ring, and you remember the number of cows you passed as you move inward. Once you're inside the deepest fence you know the number of cows at each ring level. Now as you travel back out again, you suddenly forget how many cows are in the fence ring you just left. A fence ring is scope. But instead of cows programmatic scope learns about and forgets variable names. Now replace the fences with SUBroutines and FUNCTIONs. SUBs and FUNCTIONs inside of other SUBs and FUNCTIONs etc. Something like:
DIM A
SUB OuterFence
DIM B
SUB Fence1
DIM C
FUNCTION Fence2
DIM D
SUB Fence3
DIM E
FUNCTION Fence4
DIM F
…
END FUNCTION
E = Fence4 ' run fence4
END SUB
Fence3 ' run fence3
END FUNCTION
C = Fence2 ' run Fence2
END SUB
Fence1 ' now execute Fence1
END SUB
OuterFence ' start executing
(Yes you can have code like this, no it's not good style!) Ok so if you're outside the outermost SUBroutine (OuterFence) the only variable you know about is "A". As you cross into the fence you suddenly know about variables A and B. As you travel inward you until you enter Fence4 and you know about A,B,C,D,E, and F. So inside Fence4 you could have a line that said
F = A + B + C + D + E
Now as you go back out (when FUNCTION Fence4 finishes running) you forget about variable F. So even though you could have the above line in Fence4, you can't have the above line in Fence3. Even thought you knew about the variable F before! You just forgot it! So the best you could do is:
E = A + B + C + D
As you continue to exit and forget variables until you're back at the OuterFence call, and the only variable you know about is A. That's scope! Why do you care? Well you could have something like:
DIM A
A = 1
SUB OuterFence
DIM A
A = 2
MSGBOX A,vbOKOnly,"Inside OuterFence"
END SUB
MSGBOX A,vbOKOnly,"Before calling OuterFence"
OuterFence
MSGBOX A,vbOKOnly,"After calling OuterFence"
Yes this is possible! You can DIM variable inside of SUBs and FUNCTIONs! What happens? Outside the OuterFence you know about A = 1. Once you enter the OuterFence subroutine you know about A = 2, once you leave the OuterFence, you forget about A=2, but you still know about A = 1. Try it! This is a useful technique, but you have to be careful! More jargon! Local (or locally scoped) variables are all the variables inside of a fence. Global (or globally scoped) variables, are variables outside of all fences!
One last thing, we mentioned just using variable names. Your program could have:
A = 12 ' without a DIM A
It's always good, as I mentioned, to declare your variables, but you can forget to do so "in the heat of programming." So how do you make sure you always declare a variable? This is where another Immediate mod command comes in useful. OPTION EXPLICIT is a statement that forces you to DIM all variables before you use them. Look up OPTION EXPLICIT in the manual. Then try this short program:
OPTION EXPLICIT
DIM A
A = 1
B = A + 1
MSGBOX B
What error message do you get? If you see this message, that tells you you forgot to DIM a variable! Now remove the OPTION EXPLICIT line and try running the program again It should work fine! Now fix your program so you have the OPTION EXPLICIT and it runs without errors.
CHECKBOX
A CheckBox is another GUI widget. If you look it up in the manual, you find that it looks a lot like our friend CommandButton. You should know now how to create a CheckBox, so we'll only discuss the new properties, methods and events. Most of these you've already played with in CommandButton. The property alignment specifies whether the text caption o the button is aligned to the left, middle or right edges of the caption field you specify (if you specify a width that is just wide enough for the caption it won't make any difference). TabStop allows you to specify whether the CheckBox will be able to receive Tab characters when you hit the keyboard Tab key. There are a couple of new Font styles you can play with (you should know how by now). The other import property is the Value property. This property lets you set whether the CheckBox is checked when you create it:
ADDOBJECT "CheckBox","MyBox",20,20,30,20
MyBox.Value = TRUE ' start the checkbox checked
You can check the box using TRUE in the Value property, and unchecked using FALSE. The default is FALSE. (You can now see how we use assignment statements to assign a value (TRUE which is actually a number) to an object property variable--it's all coming together!) To see if a CheckBox is checked, you can use he code:
DIM CheckBoxState
…
CheckBoxState = MyBox.Value
Here we're assigning the value of the Value property to the CheckBoxState variable. The value returned is a number instead of TRUE/FALSE. The numbers are 1 = TRUE, and 0 = FALSE.
There are no new Methods to discuss with CheckBox. The only new Event is the Change Event. This event is triggered (calls your Change event handler) whenever the user toggles he CheckBox from true to false or back.
SUB MyBox_Change
MSGBOX "Someone tapped on me!"
MSGBOX MyBox.Value
END SUB
For next time here's a simple assignment (excuse the pun):
Write a program that has:
- A command button labeled "Count"
- 2 CheckBox objects labeled "Left" and "Right"
The program should:
- Display a message box whenever the user taps on a check box. The message should display the name of the check box being tapped.
- Display a message showing the total number of times the user tapped either of the checkboxes.
- Use a variable to keep track of the number of times the user tapped either of the checkboxes.
Next time, we'll go over Strings and text (really another form of variable), and talk about ComboBoxes!
