Programming Gambas from Zip/ThreeThings

From Wikibooks, open books for an open world
Jump to navigation Jump to search

What Computers Can Do[edit| edit source]

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.

Memory[edit| edit source]

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:

Name Description Example Default
Boolean True or False 1 = 2 False
Integer A whole number from -2147483648 ... +2147483647 123 0
Float A number with a decimal part, like 2.34 2.34 0.0
Date Date and time,each stored in an integer . Null
String Letters and digits and symbols strung together. Text. "ABC 12%" Null
Variant Any datatype. It has to be converted to some type before it can be used. Null

Calculate Speed from Distance and Time[edit| edit source]

Speed Calculator Form Speed Calculator, running

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:

Gambas Error Message

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.

Conditional Execution[edit| edit source]

If..Then..Else — Game of HiLo[edit| edit source]

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!”.

HiLo Window HiLo Game, running

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:

Screenshot of Links on a Gambas help page
Screenshot of Links on a Gambas help page

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 Choices[edit| edit source]

Grading Student Marks[edit| edit source]

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.

We’ll skip having a column for student names for now. And we’ll avoid using many IF...THEN... statements in favour of its big brother, SELECT...CASE…
GambasPic120 GambasPic123

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[2] is row 2. tvMarks.Columns[1] is the whole of column 1.

A Nice Addition — Colour alternate rows[edit| edit source]

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.

Numbering Colours[edit| edit source]

LCD Screen Closeup
A computer screen is full of little lights that light up Red, Green and Blue.

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 Menu[edit| edit source]

Section of Gambas IDE

Adding a File Menu in Gambas IDE
Adding a File Menu

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.

Gambas Menu Editor
Adding a Quit menu item
Adding Ctrl-Q as a keyboard 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

Repetition[edit| edit source]

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 … Next[edit| edit source]

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 … Until[edit| edit source]

Repeat
        'do something
Until x > 50

While ... Wend[edit| edit source]

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 Button[edit| edit source]

Moving Button form

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 Numbers[edit| edit source]

Tableview on a tall form Tableview to add up 5 numbers

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 Tags[edit| edit source]

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 Table[edit| edit source]

Form showing a tableview Tableview on form, running

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
Programming Gambas from Zip
 ← Grammar ThreeThings Arrays →