Programming Gambas from Zip/Modules
Modules and Classes
editPrograms tend to become large as more features are added to them. More menu items mean more menu handlers. More buttons, more lists, more tables—all mean more subs. There needs to be a way to organise them, and there is. The files in a computer are organised into folders. The subs in a program are organised into modules and classes.
Modules are like containers. Classes are like animals of various species.
You can put what you like into containers. You can collect all the subs that are related in some way and put them into a module. For example, you might make a module called Time and put in it all the bits of program related to times and dates. In it you might put that great function you wrote to work out a person’s age given their date of birth. You called it
Public Sub AgeThisYear(DOB as date) as string
And with it you could put that function that takes how many seconds you took to complete a puzzle and convert it into minutes and seconds format:
Public Sub TidyTime(Secs as integer) as string
They could go into the Time module to save cluttering up the form’s code. There will be enough event handlers to fill it up without these functions as well. You cannot move those event handlers into a module. They have to be in the form’s code, waiting for something on the form to do something to make them fire.
How do you call on subs that have been put into a module? You have to refer to the module they have been put in, and the name of the sub. It is like having a crowd of people all gathered together in a park. You can call out “John!” or “Mary!” and John or Mary will step forward. Once you start putting people into houses, though, it has to be “HollyCottage.John” or “FernyVale.Mary”. There might be several Johns or Marys, for one thing. So we would refer to
Time.AgeThisYear(“1/6/1955”)
and
Time.TidyTime(258)
Anything in a module is available throughout your program. Modules are like boxes or folders or filing cabinets: just places you can park your subs. To call them, put the module name, a dot, then the sub.
Classes, though, are like kinds of animals. In the animal world, species are grouped into genuses, genuses into families, and so on up to Kingdom (and now one higher level, Domain), which used to be Animal or Plant but now includes others (Monera which is mainly bacteria, Protists which include algae and protozoans, and Fungi which is mushrooms, moulds and yeasts). Every animal and plant has a two-part name made up of Genus and Species. “Genus” means “General” and “Species” means “Specific”. My name is, let us say, Luke Bilgewater. There are many different individuals in the Bilgewater family, but only one Luke Bilgewater. Luke is the specific name and Bilgewater is the general or generic name. The classification system goes this way:
DOMAIN → KINGDOM → PHYLUM → CLASS → ORDER → FAMILY → GENUS → SPECIES
You will notice that “Class” comes in there. In programming, classes are things that you can have examples of. A tableview is a class. You can have many tableviews in a program. A button is a class. You can have many buttons. A menu is a class. You can have many menus. A form is a class. You can have many forms.
You can also derive classes from other classes.
Let’s take the horse. Wikipedia says, “At present, the domesticated and wild horses are considered a single species, with the valid scientific name for the horse species being Equus ferus”. Imagine a horse with wings. It would have everything that regular horses have (properties, like tails and hooves) and can do everything that regular horses do (methods, like gallop and neigh) and respond to things regular horses respond to (events, like approaching a water trough or getting saddled up). However, in addition to these, it will have wings. This new class, Equus Pegasus, will inherit everything that an Equus has, but will also have Public Sub Wings() … End specific to this special type of Equus.
Classes (unlike modules) can have as many real live examples as you want. Each example will have its own wings. Each will have its own name. “Make a new example of one of these kinds of animals” is, in programming language, “instantiate” or “make a new instance”.
A class is an abstract thing (like a type of animal). You need instances of the class to have anything you can work with.
In saying that a class definition is like a blueprint, that is the usual case. If you want the class to exist as a one-of-a-kind animal, you can do that by making it Static. Gambas has static classes. They are classes that are always there. Modules are static classes: always there, and you only have one of them.
An example of a static class is Key. In the online help it says, about Key, “This class is used for getting information about a key event, and contains the constants that represent the keys. … This class is static.” It is always there; it is a once-off thing; refer to it as if it were the name of a beast, not just the kind of beast that it is. So the key that was just typed is Key.Text and the number code for that key is Key.Code . Just as sure as horses have legs, Key has several constants you can refer to, like Key.Enter and Key.Return and Key.Del and Key.Esc that are the code numbers for those keys. And just as sure as horses can have a saddle on or a saddle off, there is the property Key.Shift and Key.Control that can be up or down, that is to say, true or false.
Let’s take a tableview and give it wings. Our new class will be everything that a tableview is, but with the additional ability to locate lines by typing in a few of the letters. It is our SearchBox again, only this time we shall make it a class. Then we can make as many new SearchBoxes as we like. We only need design the prototype for the new car; after that we can have as many rolling off the assembly line as we like.
Making a SearchBox Class
edit
The File Menu has two entries, Go Down and Quit. Don’t give Go Down a keyboard shortcut. (If typed while in a cell you can find yourself in an endless loop—nothing happens, nothing responds.) The menu items are called MenuGoingDown and MenuQuit respectively.
The menu item Go Down uses its checkbox. If ticked, searching for names by typing is switched off. Enter moves the cursor down to the next cell below as usual. If unticked (the program starts this way), type a few letters to locate a person’s name and press Enter to enter the score. Then press Enter again to leave the cell and be ready to search for the next name. The tableview property that is critical here is NoKeyboard. The menu item sets or unsets it.
Steps:
- Start a new graphical application.
- Create a new class, make it Exported and call it “SearchBox”. (Rt-click the Sources folder > New > Class...)
- Enter the line INHERITS TableView right at the top of the class.
- Press F5 to run the application. Quit the program straight away. Now there should be a SearchBox among the classes. Drag one onto the form FMain. You have just made a new instance. You can peel off as many copies as you want. For the moment, we only need the one. Now we teach our horse to fly. This code goes in the SearchBox class:
Export 'Without this you will not see the class in the toolbar of classes
Inherits TableView
Private ss As String
Public SearchColumn As Integer
Event EnterOnLine
Public Sub CheckKey()
Select Case Key.Code
Case Key.Esc, Key.BackSpace, Key.Del
ss = ""
Me.UnSelectAll
Case Key.Enter, Key.Return
Raise EnterOnLine 'action on pressing Enter
ss = ""
Case Key.Tab
SearchDown
Case Key.BackTab
SearchUp
Case Else
ss &= Key.Text
SearchDown
End Select
End
Private Sub SearchUp()
Dim i, Start As Integer
If Me.Rows.Selection.Count = 0 Then Start = -1 Else Start = Me.Rows.Selection[0] 'the selected line
For i = Start - 1 DownTo 0
If InStr(LCase(Me[i, SearchColumn].text), LCase(ss)) Then
Me.Rows.Select(i)
Return
Endif
Next
For i = Me.Rows.max DownTo Start
If InStr(LCase(Me[i, SearchColumn].text), LCase(ss)) Then
Me.Rows.Select(i)
Return
Endif
Next
End
Private Sub SearchDown()
Dim i, Start As Integer
If Me.Rows.Selection.Count = 0 Then Start = -1 Else Start = Me.Rows.Selection[0]
For i = Start + 1 To Me.Rows.Max 'if no selected line, start at top, else start at next line
If InStr(LCase(Me[i, SearchColumn].text), LCase(ss)) > 0 Then
Me.Rows.Select(i)
Return
Endif
Next
For i = 0 To Start 'if no more occurrences, you will end up at the line you are on
If InStr(LCase(Me[i, SearchColumn].text), LCase(ss)) > 0 Then
Me.Rows.Select(i)
Return
Endif
Next
End
Public Sub HandleClick()
If Me.Column = SearchColumn Then Return 'searchable column is not editable by clicking
ss = ""
Me.Edit
End
This above code is now part of all searchboxes. It still has to be called upon at the right times. If not, it will never get done. So here is the code for the main form FMain:
Public Sub sb1_EnterOnLine()
sb1.Column = 1
sb1.Edit
End
Public Sub Form_Open()
Dim Names As New String[]
Dim i As Integer
Names = ["Mereka AIKE", "Ernest AIRI", "John AME", "Stanley ANTHONY", "Natasha AUA", "Veronica AUFA", "John Taylor BUNA", "Romrick CLEMENT", "Philomena GAVIA", "Richard GHAM", "Gerard BUZOLIC", "John HEARNE", "Thomas EDISON"]
sb1.Rows.Count = Names.count
sb1.Columns.Count = 2
For i = 0 To Names.Max
sb1[i, 0].text = Names[i]
If i Mod 2 = 0 Then
sb1[i, 0].Background = &hDDDDFF
sb1[i, 1].Background = &hDDDDFF
End If
Next
sb1.Columns[0].Width = 140 '-1 for max needed width
sb1.Mode = Select.Single
sb1.NoKeyboard = True 'start with sb1 selecting the line when Enter is pressed in a cell
sb1.Expand = True
sb1.SetFocus
End
Public Sub MenuQuit_Click() 'Yes, I put in a Quit menuitem in a File menu.
Quit
End
Public Sub sb1_KeyPress()
sb1.CheckKey()
End
Public Sub sb1_DblClick()
sb1.Edit 'to edit the names in the searchable column, double-click one of them
End
Public Sub sb1_Save(Row As Integer, Column As Integer, Value As String)
sb1[Row, Column].text = Value
End
Public Sub sb1_Click()
sb1.HandleClick
End
Public Sub MenuGoingDown_Click()
MenuGoingDown.Checked = Not MenuGoingDown.Checked
sb1.NoKeyboard = Not sb1.NoKeyboard
End
This horse knows how to fly. This tableview, now known by the illustrious and noble name of SearchBox, knows how to search for occurrences of what you type.
You talk to the horse when the horse is listening. You talk to the SearchBox when it gives you events that you can intercept. Our particular SearchBox, sb1, gives us all the events that tableviews do, and one more. It has a homemade event EnterOnLine.
The horse does what you tell it if you use words it understands. When the form gets a keypress event from the searchbox, tell it to CheckKey. SearchBox knows how to check your key. SearchBox will happily scan upwards or downwards looking for the next occurrence of what you typed. If you are happy with the selected line it presents to you, it will notify you with the EnterInLine event. Otherwise it adds the key to the string of letters you have already typed and does some more searching, starting with the next line.
EnterOnLine is raised when the key you type gets checked. If you typed Enter or Return, the EnterOnLine event occurs. In the main window you decide what you are going to do about it (if anything). In our case, it means we have found the line we are after and we want to type in the second column.
- In a sense, you talk to the class through events and the class talks to you through events that it itself raises.
Homemade events that your classes give you might be like a simple greeting (“Hello!”) or they can convey parameters (“Hello, I’ll be there in 5 minutes!”). Our EnterOnLine might tell us which line you pressed Enter on. Then the Event definition would then read
Event EnterOnLine(LineNum as integer)
And the event handler might read
Public Sub sb1_EnterOnLine(LinNum As Integer)
sb1.Row = LinNum
sb1.Column = 1
sb1.Edit
End
When a line is highlighted its Row property is set to that row anyway, so it is not necessary. Like, why give a command to move to a row when you are already at that row? So the LinNum parameter is not necessary, but the horse talks to you through events it raises and it could tell you what line you are on when it sends you an event if you wanted it to.
Setting Up a Class
editIf there are things to do when a new instance of a class is created, like setting up how many rows and columns there should be in a tableview or the names of the column headings, the place to do it is in a special sub called _new() . Whenever a new example of the class is made with the New operator, this event is called.
In the SearchBox program above, we can take all the setting up that is done in the Form_Open() event handler and move it into the class itself. Delete the Form_Open() event, and in the SearchBox class put the code there. When you run the program it works exactly the same.
Now the class sets itself up. The form does not have to set up each one.
Public Sub _new()
Dim Names As New String[]
Dim i As Integer
Names = ["Mereka AIKE", "Ernest AIRI", "John AME", "Stanley ANTHONY", "Natasha AUA", "Veronica AUFA", "John Taylor BUNA", "Romrick CLEMENT", "Philomena GAVIA", "Richard GHAM", "Gerard BUZOLIC", "John HEARNE", "Thomas EDISON"]
Me.Rows.Count = Names.count
Me.Columns.Count = 2
For i = 0 To Names.Max
Me[i, 0].text = Names[i]
If i Mod 2 = 0 Then
Me[i, 0].Background = &hDDDDFF
Me[i, 1].Background = &hDDDDFF
End If
Next
Me.Columns[0].Width = 140 '-1 for max needed width
Me.Mode = Select.Single
Me.NoKeyboard = True 'start with sb1 selecting the line when Enter is pressed in a cell
Me.Expand = True
Me.SetFocus
End