Programming Gambas from Zip/ThreeThings
What Computers Can DoEdit
The code in this section is Gambas. The last section was pseudocode.
There are three things computers can do: memory, repetition and conditional execution. Repetition is doing things over and over like working through millions of numbers or thousands of records. Conditional execution means making choices.
Calculators sometimes have M+ and MR keys. Whatever number is showing goes into a memory when you press M+. Whenever you need to use that number again, to save typing it, press the Memory Recall button, MR. The memory is like a note you have made to yourself to remember this number.
Computers have as many memories as you like and you give them names. Putting something in a memory is as easy as typing Age = 23. What is on the right is put into the memory on the left. To see what is in the memory called Age, print it or put it in some place where you can see it. Label1.text = "Your age is " & Age .
Before you can use some name as a memory you must tell Gambas what kind of thing will be stored in it. This is what the DIM statement does. You declare it before it is used. For example, Dim Age As Integer or Dim FirstName as String . Memories are called Variables or Properties if they are associated with something.
You can declare a memory and put something into it in one line:
Dim FirstName as String = "Michael"
You can use memories to calculate something, and put the answer into another memory:
Dim Speed as float = 45 Dim Time as float = 1.5 Dim Distance as Float = Speed * Time Label1.text = Format(Distance, "##0.##") & "km/hr"
To DIMension things you need to know what types are allowed. Here is a list of data types:
||True or False||1 = 2||False|
||A whole number from -2147483648 ... +2147483647||123||0|
||A number with a decimal part, like 2.34||2.34||0.0|
||Date and time,each stored in an integer .||Null|
||Letters and digits and symbols strung together. Text.||"ABC 12%"||Null|
||Any datatype. It has to be converted to some type before it can be used.||Null|
Calculate Speed from Distance and TimeEdit
There are six labels, three textboxes and one button. The names for the labels do not matter, but the textboxes and the button are named as shown. The program is:
Public Sub bCalculate_Click() tbSpeed.text = Val(tbKilometres.text) / Val(tbHours.text) End
“Val” takes a string and converts it to a number. It means “the value of”. It could be written this way, using variables, but it would take more lines:
Public Sub bCalculate_Click() Dim d As Float = Val(tbKilometres.text) Dim t As Float = Val(tbHours.text) Dim s As Float = d / t tbSpeed.text = s End
If you want to give better names to the variables,
Public Sub bCalculate_Click() Dim distance, time, speed As Float distance = Val(tbKilometres.text) time = Val(tbHours.text) speed = distance / time tbSpeed.text = speed End
And you could write a function that takes distance and time and returns the speed. It is good to teach the computer things. Here we have taught the computer that Speed(5,2) is 2.5. Our calculate button uses it. We could have a menu item that also uses it.
Public Sub bCalculate_Click() tbSpeed.text = Speed(Val(tbKilometres.text), Val(tbHours.text)) End Public Sub Speed(d As Float, t As Float) As Float Return d / t End
Now let’s trick our program. Don’t put in anything for the distance or time. Just click the Calculate button. The poor thing cannot cope. We get this error:
Problems come at the extremes. In repeated sections, they are most likely to occur in the first or the last repetition. Here, there is an extreme input: nothing, zip, nilch. Gambas cannot get the Val( ) of that.
tbKilometres.text was Null. We should anticipate that someone might click the button without putting in any numbers. Here are two ways handle the situation, and the second one is better because ‘prevention is better than cure’:
1. Finish early (Return from the sub early)
Public Sub bCalculate_Click() Dim d, t, s As Float If IsNull(Val(tbKilometres.text)) Then Message("Hey, you there! Give me a NUMBER for the kilometres.") Return Else d = Val(tbKilometres.text) Endif If IsNull(Val(tbHours.text)) Then Message("A number would be nice for the hours. Please try again.") Return Else t = Val(tbHours.text) Endif s = d / t tbSpeed.text = s 'To round to 2 decimal places, change s to format(s,"#.00") End
2. Only enable the button if the calculation can proceed. Set the Enabled property of the button to False to begin with. We need to handle a few events.
Public Sub bCalculate_Click() tbSpeed.text = Val(tbKilometres.text) / Val(tbHours.text) Fmain.SetFocus 'otherwise the button stays highlighted; focus the form End Public Sub tbKilometres_Change() CheckBothAreNumbers End Public Sub tbHours_Change() CheckBothAreNumbers End Public Sub CheckBothAreNumbers() bCalculate.Enabled = Not IsNull(Val(tbKilometres.text)) And Not IsNull(Val(tbHours.text)) End
bCalculate.Enabled = means ‘set the enabled property of the button to...’
Not IsNull(Val(tbKilometres.text)) means Yes if the text in the kilometres textbox can be converted to a number.
Every textbox has a Change event. It fires when the text in the box changes. Every time you press a key, that Change event is going to see if it’s time to enable the bCalculate button.
And because I cannot leave well enough alone, I apologise for introducing the “Group” property. You may need coffee. Or skip this section. Do you notice that the two textboxes have to each handle the Change event the same way? tbKilometres and tbHours both check for numbers and enable or disable the button accordingly. Wouldn’t it be nice if both textboxes were like just one textbox? Gambas can do this. Put them in a Group. Then this single group will have a Change event, and you can handle it just once. Group is a property. Find the Group property for each and set it to “InputBox”. Now your code becomes the simplest yet:
Public Sub bCalculate_Click() tbSpeed.text = Val(tbKilometres.text) / Val(tbHours.text) Fmain.SetFocus 'otherwise the button stays highlighted; focus the form End Public Sub InputBox_Change() CheckBothAreNumbers End Public Sub CheckBothAreNumbers() bCalculate.Enabled = Not IsNull(Val(tbKilometres.text)) And Not IsNull(Val(tbHours.text)) End
It is as if the two textboxes have become inputboxes (a name you just invented), with all the same events. One set of event handlers for several objects.
It’s robust (resistant to users who insist on not using your application the way you expect them to). It’s concise (3 event handlers, 4 lines of code). It works. One thing remains: a button that says QUIT with one thing in its Click event: the command Quit. Over to you.
If..Then..Else — Game of HiLoEdit
This game is easy to play: one person thinks of a number between 1 and 100. You try to work out the number in as few guesses as possible. Each time you will be told “Too High!” or “Too Low!”.
There are three labels and one textbox. The large label where it says “Guess...” is named labMyReply. The textbox is named tbGuess. The small label in the bottom left corner is named labCount.
Public MyNumber As Integer Public Sub Form_Open() MyNumber = Rand(1, 100) End Public Sub tbGuess_KeyPress() If Key.Code = Key.Enter Or Key.Code = Key.Return Then GiveMessage End Public Sub GiveMessage() If IsNull(tbGuess.text) Then 'no guess at all labMyReply.text = "Guess..." Return Endif Dim YourGuess As Integer = Val(tbGuess.text) If MyNumber = YourGuess Then labMyReply.Text = "Right! " & MyNumber MyNumber = Rand(1, 100) tbGuess.text = "" FMain.Background = Color.Green labCount.text = "0" Return Endif If YourGuess > MyNumber Then labMyReply.Text = "Too High!" Else labMyReply.Text = "Too Low!" tbGuess.text = "" FMain.Background = Color.Pink labCount.text = Val(labCount.text) + 1 End
The program checks the guess when ENTER or RETURN is pressed in the textbox. The Key class is static, meaning it is always there—you do not have to declare or create it. If you ever need to check for some key, such as SHIFT-C, being pressed, you would write:
If Key.SHIFT And Key.Text = "C" Then GiveMessage Stop Event Endif
The Stop Event line is there to prevent capital-C being printed in the textbox, which would normally happen when you type Shift-C in a textbox.
Public MyNumber As Integer means we want a public property called MyNumber. You could say Private instead of public. Private properties are accessible only in the code belonging to that form. If we had other forms (i.e. windows) they cannot see another form’s private property. Declaring a property as private is a way of telling you, the programmer, that you have only used this property here, in this form’s code that you are looking at. Also, another form could have its own property and use the same name.
In the Event Form_Open() event handler, Rand(1,100) is a built-in function that represents a random number between 1 and 100.
In the Event tbGuess_KeyPress() event handler, the key just typed is compared with the Enter and Return keys. If it was either one, the guess is checked. Every key has a number. Enter is 16777221 and Return is 16777220. We do not need to know the numbers, because they are stored in Key.Enter and Key.Return. These are constants in the Key class. Because the Key class is static we do not have to make a new example of it: it is always there and we can refer to it by its name. We never need another one of them. There is only ever one keyboard. We never have to say “the Key belonging to the ACER laptop keyboard” or “the Key that was typed on the HP laptop keyboard”.
If IsNull(tbGuess.text) Then … End avoids the nasty situation of a person pressing Enter without having typed in any number at all.
Dim YourGuess As Integer = Val(tbGuess.text) puts the numeric value of what was typed into a variable called YourGuess. This is an integer. If a person typed 34.56, only 34 would be put into YourGuess.
If MyNumber = YourGuess Then… checks to see if YourGuess matches MyNumber. If it is, show the “Right!” message and make the background colour of the form green. Choose another random number ready for the next game and then Return, because nothing more needs to be done.
If YourGuess > MyNumber Then labMyReply.Text = "Too High!" Else labMyReply.Text = "Too Low!" puts the appropriate message into the labMyReply textbox. This is an If...Then...Else statement all on one line. In either case, continue on to make the background pink.
From the Gambas help page, these are the properties and constants for the Key class:
The constants return integer numbers. The properties are what was typed. So you could check if the user typed the PgDown key with if Key.Code = Key.PgDown then… Negotiating and reading the help pages is a skill in itself.
Select … Case … — Many ChoicesEdit
Grading Student MarksEdit
Let’s type numbers into a TableView and make it work out whether each student earns an A, B, C, D, E or F grade. We’ll need two columns. The first will be for the marks, the second for the grades.
The TableView is named tvMarks. The textbox is named tbRows. Double-click an empty part of the form and enter the code for the form’s open event:
Public Sub Form_Open() tvMarks.Columns.count = 2 End
Double-click the textbox and enter:
Public Sub tbRows_KeyPress() If Key.Code = Key.Return Or Key.Code = Key.Enter Then tvMarks.Rows.Count = Val(tbRows.Text) End
Right-click the TableView > Event > Click, then right-click > Event > Save, and enter these:
Public Sub tvMarks_Click() If tvMarks.Column = 0 Then tvMarks.Edit End Public Sub tvMarks_Save(Row As Integer, Column As Integer, Value As String) tvMarks[Row, Column].Text = Value Dim x As Float = Val(Value) Select Case x Case 90 To 100 tvMarks[Row, 1].Text = "A" Case 80 To 89 tvMarks[Row, 1].Text = "B" Case 70 To 79 tvMarks[Row, 1].Text = "C" Case 60 To 69 tvMarks[Row, 1].Text = "D" Case 50 To 59 tvMarks[Row, 1].Text = "E" Case Else tvMarks[Row, 1].Text = "F" End Select End
The tvMarks_Click() handler lets you type in the cell if the column is 0. If you click in column 1 you will not be able to type: nothing happens when you click.
You might think that whatever you type in a cell should show up. It doesn’t. It raises the Save event. You might want something else to appear other than what was typed. During the Save event, actually put the text that was typed into the text property of the cell:
tvMarks[Row, Column].Text = Value
The Save event comes with three parameters that you can use freely in the course of the event handler: tvMarks_Save(Row As Integer, Column As Integer, Value As String) . This line of code puts the value that was typed into the text of the cell. Which cell? tvMarks[row, column]. That is the cell.
You refer to a cell by using square brackets: tvMarks[1,0] refers to row 1, column 0. (Remember rows and columns are numbered starting with zero.) tvMarks.Rows is row 2. tvMarks.Columns is the whole of column 1.
A Nice Addition — Colour alternate rowsEdit
The TableView_Data() event is very useful. It is raised (happens) every time a cell needs to be redrawn on the screen. (It is useful to remember it deals with cells, not rows or columns or the whole table.) Right-click the tableView, then > Event > Data and enter this:
Public Sub tvMarks_Data(Row As Integer, Column As Integer) If Row Mod 2 = 0 Then tvMarks[Row, Column].Background = &F0F0FF End
This gives alternate rows a light blue colour (very pretty). To explain, cells have a property called “background”. It is a colour. Colours can be described in several ways: using a straight number is the simplest. The number is &F0F0FF.
What sort of number is &F0F0FF? The “&” sign means the number is written in hexadecimal. Normally we use the decimal system, which is Base 10. You count from zero to nine and the digit goes back to zero and you increase the digit to its left by one. Here is normal counting in Base 10: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,16… Using hexadecimal you have 16 digits, not ten. Here is counting in Base 16: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20, 21… The eleventh number in the decimal system is written 11. The eleventh number in the hexadecimal system is written B.
Now, what colour is &F0F0FF? The hexadecimal number F0F0FF is actually three 2-digit numbers: F0, F0, FF. The first number is how much Red. The second number is how much Green. The third number is how much Blue. That is RGB, red-green-blue. Each goes from 00 to FF. 00 in the first place would mean “No red at all”. FF in the first place would mean “Maximum red!”. Pure red would be &FF0000. Pure green would be &00FF00, and pure blue is &0000FF. So pure black is &000000. Pure white is &FFFFFF, which is all the colours as bright as you can make them.
This colour is grey: &F0F0F0. The red, green and blue lights are mostly on, but not fully bright. F0 is not as bright as FF. To get a darker grey, lower the numbers but lower them all the same, e.g., C0C0C0. Darker again, B0B0B0. The tiniest bit darker than that is AFAFAF, because after AF comes B0. The point is, when they are all the same you get shades of grey. So look at F0F0FF. It is a very light grey (F0F0F0), but the last number, the one for Blue, is a bit brighter. It is FF. So the colour is a very light grey with the Blue just a bit brighter. That is, it’s very light blue. It is all about the mix of three colours, red, green and blue. This is pale pink: FFD0D0. This is pale green: D0FFD0. This is pale yellow: FFFFD0. (Red=brightest, Green=brightest, Blue=a little less bright). This is full yellow: FFFF00.
It is all about the tiny little LED lights on your screen. There are millions of them, but they are grouped in threes—a red, a green and a blue. You are control the brightness of each little light. The brightness goes from 00 to FF, or in decimal, from 0 to 255. (FF=255). One red, one green and one blue act like one coloured dot. It is called a pixel. A typical laptop screen has a resolution of 1366 x 768 pixels. That is 1,049,088 pixels. Each has three little LED lights, making 3,147,264 lights on your screen, each one with 256 shades of brightness. Amazing!
Adding a Quit MenuEdit
Click “+Insert”. Name this menu MenuFile and caption it File. The caption is what the user sees. The name is how we refer to the menu in our programming.
Click “+Insert” again. We want it to be part of the File menu, so click the push-right button on the toolbar. While you are at it, give the menu CTRL-Q for a shortcut.
Now write a handler for when the user clicks on MenuQuit. (To do this, look for the File menu in the form and click it, then click the menuitem Quit.)
Public Sub MenuQuit_Click() Quit End
Repeated sections of code are called loops. There are a few different ways to repeat.
Gambas help lists them on http://Gambaswiki.org/wiki/cat/loop
Counting a fixed number of times:
For … NextEdit
For i as integer = 1 to 5 'do something Next
i is an integer that counts 1, 2, 3, 4, 5. Don’t put “as integer” if you already have DIM i As Integer
You can use i in the loop, but do not change its value. The word NEXT increases it by one and sends the computer back to the start (the FOR line) where i is checked to see if it is bigger than 5. If it is, it goes to the line following NEXT.
Repeat … UntilEdit
Repeat 'do something Until x > 50
While ... WendEdit
While x < 50 'do something Wend
To exit from a loop, BREAK. To exit from a sub, RETURN. To go to the next repetition, CONTINUE.
There is also the infinite loop, DO ... LOOP, and for items in a numbered list, FOR EACH ... NEXT.
The Moving ButtonEdit
Public Sub Button1_Click() Do Repeat 'move to the right Button1.x += 1 Wait 0.001 Until Button1.x + Button1.w = FMain.Width Repeat 'move to the left Button1.x -= 1 Wait 0.001 Until Button1.x = 0 Loop End Public Sub Form_KeyPress() Quit End
The program ends when you press a key. Wait 0.001 delays progress for one-thousandth of a second. The delay allows the button to be redrawn. X and Y are traditionally used for Across and Down. The button moves from left to right and back, so it is Button1’s X that we need to change. The button keeps moving to the right until its left side plus its width is equal to the width of the form. In other words, it stops moving right when the right side of the button meets the right edge of the form. After that we start subtracting from X until it meets the left edge of the form. Try changing the size of the window while the button is in motion: the button still moves to the edge.
A Tableview That Adds Up To 5 NumbersEdit
Type any numbers, ending each with Enter to go to the next line. The total updates when you press Enter.
Public Sub Form_Open() tv1.Rows.Count = 6 tv1.Columns.Count = 1 End Public Sub tv1_Save(Row As Integer, Column As Integer, Value As String) Dim t As Integer tv1[Row, Column].Text = Value For i As Integer = 0 To 4 If IsNull(tv1[i, 0].text) Then Continue If Not IsNumber(tv1[i, 0].text) Then Continue t += Val(tv1[i, 0].text) Next tv1[5, 0].Background = Color.LightGray tv1[5, 0].Foreground = Color.Blue tv1[5, 0].RichText = "<b>" & t & "</b>" 'tv1[5, 0].text = t End Public Sub tv1_Click() If tv1.Row < 5 Then tv1.Edit End
Notice how the totals cell is blue on light grey, and the text is bold. "<b>" & t & "</b>" are tags each side of the total. Rich text is text with tags in it. The first tag is “switch bold on” and the second, with the slash, is “switch bold off”.
The Save event occurs when Enter or Return is pressed. After putting the typed number into the cell with tv1[Row, Column].Text = Value, the new total is put into the richtext of cell tv1[5, 0].
Rich Text TagsEdit
Gambas allows these tags in rich text. They are part of HTML, HyperText Markup Language, which web browsers use to display web pages. Each is switched on first, then switched off at the end, e.g. "<b><i>This is Bold Italic</b></i>".
|<h1>,<h2>, <h3>, <h4>, <h5>, <h6> →Headlines||<sup> Superscript|
|<b> Bold font||<small> Small|
|<i> Italic||<p> Paragraph|
|<s> Crossed out||<br> Line break|
|<u> Underlined||<a> Link|
|<sub> Subscript||<font> Font|
The Font tag is used this way: "<Font Color=#B22222>" & "<Font Face=Candara>" & t & "</Font>"
The Paragraph tag denotes paragraphs, which are usually separated by a blank line by browsers. It can used this way: <p align=right>Some Text</p> but on one web page, referring to HTML, it said, “The align attribute on <p> tags is obsolete and shouldn't be used”.(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p). In the meantime, it works.
|<tt> single-line source text||<pre> preformatted text (preserves spacing)|
|<hr> horizontal line (no end tag needed)||<ul> unsorted list|
|<li> list||<ol> list|
<ul><li>Jim</li><li>John</li><li>Ann</li></ul> will give bulletted points in a vertical list.
<ol><li>Jim</li><li>John</li><li>Ann</li></ol> will give numbered points in a vertical list.
A Tableview That Adds Every Number In The TableEdit
This program adds numbers as you type them in a 4 x 5 grid.
Public Sub Form_Open() tv1.Rows.Count = 5 tv1.Columns.Count = 4 End Public Sub tv1_Save(Row As Integer, Column As Integer, Value As String) Dim i, j, t As Integer tv1[Row, Column].Text = Value For i = 0 To tv1.Rows.Max For j = 0 To tv1.Columns.Max If IsNull(tv1[i, j].text) Then Continue If Not IsNumber(tv1[i, j].text) Then Continue t += Val(tv1[i, j].text) Next Next Label1.Text = t End Public Sub tv1_Click() tv1.Edit End
The loops are inside each other. They mean “For every row, zip across the columns”.
For i = 0 To tv1.Rows.Max 'for every row going down... For j = 0 To tv1.Columns.Max 'go across every column ... Next Next