Gambas Programming Beginner's Guide, Book Three: Gambas Lines of Code
Gambas Programming Beginner's Guide, Book Three: Gambas Lines of Code
Contents
* Before we begin
* Three fundamental questions
* A little about designing an application
* The DirLister Application
* Why DirLister
* The DirLister How
* The DirLister What
* The Chain of Events
* DirLister: Listing directories and files
* The listing process
* The Listing Procedures
* Showing the Results and Saving
* The Status Bar
* Designing the Help System
* Final words *
* Contents * EOF *
[D 05-04-2020 h 09:40]
[L 06-04-2020 h 11:30]
Before we begin
Over the millenia, "Know thyself" proved to be far more difficult than
building various toys such as buildings, cars, planes and other vessels (navy,
cosmic vehicles, orbital stations) and the latest gadget of the last two decades,
obviously, the "pocket computer": the so-called "Smart Phone".
At this point, I can only say that I use a computer to help me develop my
inner skills, either existing ones or new ones. Your skills, my skills, are the
only real assets that really mean something and it worths going as far as we
can on that path.
Being organised, having a clear mind, being able to write a clear plan
that you can follow and that leads you to the achivements you desire, is a key
skill.
Now, let's imagine a simple tool: a traffic light (semaphore, if you like).
It shows three colors:
The previous two books of this series, cover exactly this aspect: the
available resources for learning the language and even more.
As for the "Red Light", it is the same in Life: if you fail, you have to learn
something you thought you know, but practice proved the opposite.
The next color, is YELLOW: "Get prepared for the next step!"
Well, in programming that means that you already learned a lot about
the language, you got quite skilled in getting the most of it, you even passed
the program logic test. Your program works, it does what it was meant to do.
You made it!
Your program might do what you meant it to do. Still, by the very fact
that you created it, by the very fact that you use it, YOU GREW, and you keep
GROWING. You developped something INSIDE YOU. That changes your
perspective. For that reason, YOU'LL SEE THINGS DIFFERENTLY.
And you'll descover that you can add a new feature that enhances the
program. And there you are, working again at it.
Only this time, everything goes faster. And the next time, even faster.
IF and WHEN that happens, that means that you developped some skill
to a level high enough to help YOU solve many problems very fast.
For me, Life and computers are very much the same.
There are three questions that I ask myself in any situation and I
recommend you do the same. I seek and find as many answers as I can, using
any available resource to do that.
Since I persented you a problem, let's see if there is any solution to it.
Step 1. Define a flag. Meaning, a variable to tell you IF there was a
[ListBox1_Click()] OR, there was an assignment for a specific item:
Dim booListBoxClicked As Boolean '--- This has to be global, so we can set it from anywhere in the program
'---
booListBoxClicked=False '--- We put it here, otherwise when the Click() event is triggered, it will be executed.
ListBox1.Item=0 '--- That's very common, select the first item. That trriggers the Click() event.
'---
Public Sub ListBox1_Click()
'--- Now, check what happened:
If booListBoxClicked=True Then
'---Do something or just ignore
Else
'--- Definitely, IGNORE EXECUTION!
Return '---We skip the event.
EndIf
End
This is how:
'---
Public Sub ListBox1_Click()
'--- Now, check what happened:
If booListBoxClicked=True Then
'---Do something or just ignore. We can write the code outside the conditional.
Else
'--- Definitely, IGNORE EXECUTION!
'--- Before exiting the event's procedure, we change the flag's value:
booListBoxClicked=True '--- Now, we can exit the procedure! If it occures again, the flag is TRUE.
Return '---We skip the event.
EndIf
'--- The procedure's code might go here, or inside the If... Outside, it's easier to write/read the code.
End
Actually, this code is a PATTERN that can be used to filter any weird situation
you encounter during development. For instance, before a Form actually shows
up on the screen, the [FMain_Resize()] event, occures THREE TIMES. Did
you know this?
NOW, you do!
So, if you write code on this event, bear in mind that it might get
executed three times. Unless... You DESIGN IT otherwise!
The following chapters will describe the things DirLister does and how it
does what it does.
While "What it does" you can read in the "DirLister User's Guide",
HOW IT DOES, goes here.
This is how you can see the code and get a good idea on how to get from
a set of requirements, to something functional, both in data processing and in
GUI design.
DirLister is available for installation within the IDE, via the startup
screen, "Gambas Software Farm", search filter "DirLister", "Download
and Install" option, OR, on Gambas Farm, at:
https://gambas.one/gambasfarm/?id=771&action=search
While I'm very much aware of the fact that there might be many better
solutions to get the same outcome, this is how I figured it out after less than
one month of practice with Gambas. Maybe, if I decide to go ahead, I'll find
different solutions, better ones or who knows what.
So far, it does what I need, I am using it and I am pleased how it works.
Theare are some features I'd like it to have, but since for now it works, I'll be
thinking of those some other time. Then decide if they are really important and
it worths working on them.
Why DirLister
When the "Why" was clearly outlined, the next step was to find out
"How am I going to achieve what I want?"
The most obvious answer, is "Read the docs!"
Well, that proved to be just a theory...
After spending well over 250 (Yeah... Two hundred and fifty +) hours
digging the internet for a few usable lines of code, I figured out the basics and
started to write some lines of code, to get a little warmed up...
First versions, were mostly sketches. I wanted to see how properties
work, what events and how to use those, what controls are available and what
do they offer and the like.
While I knew what was the way I needed to look like, the way to get
there, proved to be longer than it was supposed to be. I'll skip the details.
The unpleasant fact is, that from an average of 180 lines of commented
and tested code written daily in RapidQ, in Gambas, I suffered a severe fall
down, to a merely 30 lines of code per day. Frustrating! Nevertheless, I went
on. That is why I wrote and published the two previous books: to spear the
next person that aproaches Gambas, from wasting 250 hours +, just to get
some basic knowledge on using Gambas.
Versions from 0.1.x to 0.4.x, were only meant for this sole purpose: to
learn how to use the different controls available, statements,
commands and other language stuff.
The design ideas, started to evolve, as I saw the means to get better
results, using different controls.
From version 0.5.x, I switched from the Form control as a container for
all things, to the TabPanel control. This offered me the room I needed for all
the other controls and features.
As you can see, the TabPanel, has four tabs and each one is in fact a
small application that does a different set of tasks, with its own interface.
And it takes only a click to move from one to the other. A big step
forward!
Now, having the looks and feel clearly outlined both in my mind an on
the IDE designer, I could move to the less obvious part: connecting the various
properties, events, methods and procedures, into something that actually
"does the thing".
Gambas, like many other programming languages, is an EVENT DRIVEN
environnement. Meaning that the application, "listens" continuously and if the
user interacts with the computer, depending on the type of event, it will
execute the code assigned to that event.
Since doing anything complex is something that requires more than one
simple action, like pressing a key on the keyboard or clicking a mouse button
or whatever other action, I had to figure out:
• The SHORTEST WAY to get to where I want;
• What can I do wrong on that path, that might cause the program to hang
or give unexpected results? This is where things get really complicated
since it is very difficult to figure out what was my mistake if any, or...
was it a bug of a control or some other language element? Besides, my
mistakes, are my mistakes. How am I supposed to know what someone
else might do wrong? Different people, different knowledge, different
level of practice, too many variables!... Complicated!
Since the documentation was of little help if none, I had to figure out by
myself how to connect "this with that" to get to a certain result. Eventually, a
stable result.
This was a step by step process that lead me to design the interface in a
certain way; then I figured out the USER actions, or "External Events" that I
was supposed to link to the "Internal Events", such as the example I gave
before, [ListBox1_Click()].
At this point, I had to figure out how can I get what I want, on the
shortest path, if there is at least one. Usually, there are more ways to do the
same thing and how you do it, largely depends on factors such as how many
lines of code you need to write, how much CPU will it use, how much RAM will
it use, how easy and intuitive would it be for the user to get things done.
And here, we get to the concept of "Chain of events".
Each and every action is a link in a chain of actions that is meant to lead
to the desired outcome. However, this chain of events is less obvious for the
user, if obvious at all...
That is why I decided to walk on this path: presenting the chain of
events, from both perspectives. User's perspective and programmer's
perspective.
This way, you will be able to ponder yourself each and every solution and
if you think there is a better way to do the same thing, well, you have the
code, you can test the idea. If it works, that's great!
The upper part, has a toolbar with four buttons. From left to right, they
are: [ListDirBtn], [HelpBtn], [RefreshBtn], [CloseBtn].
I wish I could say it is only one chain of events, but as you already
figured out I guess, there is more than one.
Let's see them:
1. User wants to list a directory of his choice: Clicks the [ListDirBtn].
The listing procedure should start at once. BUT! What if...? We'll se the
catch here, below.
2. User wants Help: Clicks the [HelpBtn]. This is strightforward and
simple. We'll see the code for that.
3. User got, for some weird reason, an ugly interface and wants to fix
this: Clicks the [RefreshBtn]. This is easy, too.
4. User wants to leave the application:. Clicks the [CloseBtn]. That
was easy, too.
As you can see, while in words and user actions things are easy, in code,
all things get more complicated!
After the User clicked the Help button, we need to make sure a few
conditions are met:
1. Do we have the file we need to open? While for me, it is supposed
the User has it, simply because I put it there, in computers, guess-
work is bad practice so, we need to test that.
2. Are we on a system that uses xdg-open to open the default
application for a certain file MIME type? We need to test that too.
3. IF, and ONLY IF all went smoothly , we launch the file using the [xdg-
open] tool.
In the end, the only thing we can do if the unexpected shows up, is catch
the error and show an appropriate message, if any available.
While this code covers all predictible events that might occure, who really
knows what else might happen, that is out of reach even for a highly skilled
programmer? So, even if it looks great, still something might happen to defy
this "looks great" paradigm... However, at least I got peace of mind: I did my
best to prevent what I could figure out.
[RefreshBtn_Click]
TabPanel1.Refresh
LeftFManPane.Reload
RightFManPane.Reload
FMain.Refresh
Wait 0.00001
End
As you can see, this was really easy: I called some control's methods and
we're done. First versions were only with [FMain.Refresh] but for reasons
beyond my understanding, that worked bad: did nothing with the
[FileChooser] and [TabPanel] controls, so I changed to this. Ugly, but it
does the job. And... frankly, the User... How does he know the code is ugly?!
There is another solution though... Using the [gb.dbus] and
[gb.inotify] components to trigger the [FileChooser.Reload] whenever a file is
added or deleted to the current directory shown by the control, or a
subdirectory was created/deleted from a File Manager, or another external
appplication...
[CloseBtn_Click()]
That's even easier:
Initially the only line of code was [Quit], but while testing I came to
understand that if the two controls were loaded with text, especially with large
amount of data, closing took a long time so, I fgured out how to say a nice
"Good bye" instead of confusing the user that expected to see the app closing
and apparently, nothing happened.
Since the list files used in many tests were above 75 MB in size, it seems
that it took a long time to free the system resources so, I "squeezed" a little
the process.
[ListDirBtn_Click()]
This event, does the "heavy weights lifting" job so I left it for a dedicated
chapter, since there are more things to explain here.
The code is larger than the others and many things are done here, so
let's see the code (on the next page).
Until next page, let me say a few words about what happens.
(1) User clicked the [ListDirBtn] button. Now, we need to know what
type of list he wanted: just the subdirectories of a certain directory or drive?
That is the default option but maybe, he forgot to make a choice and wanted a
different result. Since tests proved that this is fast even on large drives, best
choice is to leave this option as DEFAULT OPTION.
The other options, are triggered too by [RadioButton] controls. Since
there are three of them, one will be selected by default so we're done here.
Nothing bad happens if User forgets to select a different option.
Next time, he will remember and click the radio button to select the
appropriate option.
The [lngItemsFound], is a global variable to store the number of the
items found on the drive and we need to reset it to zero.
The Dialog class has a bug and if i use once a call
[Dialog.OpenFile()] or [Dialog.SaveFile()], using a filter for a speficific file
type, the class freezes as "File Dialog", disabling the directories, regardless the
fact that the call is [Dialog.SelectDirectory()] and this should NEVER
happen, since the filter was set for the OPEN or SAVE FILE.
lngItemsFound = 0
Dialog.Filter = [""]
Dialog.Title = "Choose a Directory to list:"
If IsDir("/win") Then
Dialog.Path = "/win/" '--- Normally, would have to be "/", or "/media"
Else
'--- We are on a different computer. Leave the default path to /media
Dialog.Path = User.Home '--- Normally, would have to be "/", or "/media"
Endif
DirTextArea.Text = ""
'Message("[ListDirBtn_Click())]: Dialog Path = <" & Dialog.Path & ">._.")
Select Case ListingType
Case "DirOnly"
'---Message.Info("You chose <DirOnly>")
If Dialog.SelectDirectory() Then Return
FirstDirPath = Dialog.Path
InfoBox.Text = "[ListDirBtn_Click()] FirstDirPath: " & "<" & FirstDirPath & ">"
'--- This is where we change behaviour according to <ListingType> parameter
ListDirOnly(FirstDirPath)
Case "DirAndFiles"
'---Message.Info("You chose <DirAndFiles>")
If Dialog.SelectDirectory() Then Return
FirstDirPath = Dialog.Path
InfoBox.Text = "[ListDirBtn_Click()] FirstDirPath: " & "<" & FirstDirPath & ">"
'--- This is where we change behaviour according to <ListingType> parameter
ListDirAndFiles(FirstDirPath)
Case "DirRecursively"
'---Message.Info("You chose <DirRecursively>")
'---Return
If Dialog.SelectDirectory() Then Return
FirstDirPath = Dialog.Path
InfoBox.Text = "[ListDirBtn_Click()] FirstDirPath: " & "<" & FirstDirPath & ">"
'--- This is where we change behaviour according to <ListingType> parameter
ListDirRecursively(FirstDirPath)
DirTextArea.Insert("================== Found " & Str(lngItemsFound) & " items so far.
==================" & Chr$(10))
End Select
Catch
Message.Info(Error.Text)
End
This is the first code sequence and what am I doing here, is to test if I'm
home. Meaning if DirLister runs on my computer. If so, I want a specific
partition to be the default choice. Else, I will default to User's Home.
After setting the default directory, we clear the [DirTextArea] control
and get ready to fill it with our data.
The commented line is a message dialog template that I can use when
something goes sideways. Here, if uncommented, it will show me the choice
made with the Dialog control. The head of the message, tells me where it
comes from: [ListDirBtn_Click()]. If there is more than ony "Spy" working as
an "operational field agent", then I need to know its location.
I use this method to pause the execution if something it's unclear, or
seems to go wrong for some reason. Say, a variable that was supposed to
have a value, is actually empty for some reason.
lngItemsFound = 0
Dialog.Filter = [""]
Dialog.Title = "Choose a Directory to list:"
If IsDir("/win") Then
Dialog.Path = "/win/" '--- Normally, would have to be "/", or "/media"
Else
'--- We are on a different computer. Leave the default path to /media, or /home/User
Dialog.Path = User.Home '--- Normally, would have to be "/", or "/media"
Endif
DirTextArea.Text = ""
'Message("[ListDirBtn_Click()]: Dialog Path = <" & Dialog.Path & ">._.")
Case "DirAndFiles"
If Dialog.SelectDirectory() Then Return
FirstDirPath = Dialog.Path
InfoBox.Text = "[ListDirBtn_Click()] FirstDirPath: " & "<" & FirstDirPath & ">"
'--- This is where we change behaviour according to <ListingType> parameter
ListDirAndFiles(FirstDirPath)
Case "DirRecursively"
If Dialog.SelectDirectory() Then Return
FirstDirPath = Dialog.Path
InfoBox.Text = "[ListDirBtn_Click()] FirstDirPath: " & "<" & FirstDirPath & ">"
'--- This is where we change behaviour according to <ListingType> parameter
ListDirRecursively(FirstDirPath)
DirTextArea.Insert("================== Found " & Str(lngItemsFound) & " items so far.
==================" & Chr$(10))
End Select
PictureBox1.Picture = Picture[strIconsRelativePath & "Info-60x60.png"]
Catch
Message.Info(Error.Text)
End
As you see from the code above, the internal events are branched. We
have three choices and for each one, we do a slightly different job, both behind
the scenes AND on the GUI, namely, in the [DirTextArea] control.
What is going in the background?
There are three procedures: [ListDirOnly(FirstDirPath)],
[ListDirAndFiles(FirstDirPath)], [ListDirRecursively(FirstDirPath)].
[FirstDirPath], contains the previously selected directory. We pass it to
the procedure, and will use it inside each procedure, but in a different manner
each time.
The last line of code in the [Select Case], [DirTextArea.Insert(...)],
writes some useful data at the end of the list: how many items were found in
the selected directory.
If there are many items in the list, you need to scroll at the end or hit
[Ctrl+End] to see the line. So, there is an example here on how to convert
numeric values into strings, then show those somewhere, or maybe write them
into a file, behind the scenes and also, how to put some data at the end of a
file, using the [DirTextArea.Insert(...)].
Obviously, the code is different if you want to do it directly in a file, since
you need to use the [Stream] class.
There are other procedures where I used this class, such as
[OpenListBtn_Click()], but they are on a different chain of events, on the
second Tab, [Search Lists] and on a different interface.
Maybe I'll cover that in another book.
Anyway, you can study the code and you can find where it appeares,
using the SearchBox in the IDE, with the "Stream" keyword. It will list all
procedures where the [Stream] class is used and you'll be able to see HOW.
Now, we spread some light on "How" we get to list based on user input,
handled by the three RadioButtons.
can be handled, some weird collisions might lead to an error number with
many figures, a LONG type variable. This usually signals an unknown error and
even if there is nothing to do, it keeps the program running, instead of
crushing it.
Since I still have to cover the error handling section when I'm writing
this, that is all I can say for now.
The next chapter, is about the three procedures that we launched from
within the [Select Case] statement.
As you can see, the "Chain of Events" approach, is slightly different
from what the user might expect and although it includes the User's
perspective, we go way beyond that. "A larger picture", so to speak.
[ListDirOnly(...)]
This is the default procedure in the [Select Case] statement.
While this might look useless now, I needed it to do another job, in the
[File Manager] mode: to set up some bookmarks. As I explained in the User's
Guide, I have a complex directory structure. For this reason, I want to be able
to set up a set of directories for any of the panels of the File Manager. It
spears me of lots of clicks to get where I want.
Another reason is that when I work a lot in a project and the files I
create get too many, I need to re-structure the contents of a certain directory
so I create a number of subdirectories. If this solves the problem, OK, but
sometimes I need to go on more than one level of depth and here is the
explanation.
Instead of clicking 10 times to get to the last subdirectory, I click on the
best match in the bookmarks list and there I am!
Besides, maybe I need to see how exactly looks the directory tree after
say, one year or maybe 10 years later from the day I created it and it's easier
with such a list. I just load it into the internal editor and that's all.
So, let's see now how is it done.
The code on the next page, starts with defining some variables we'll use
here, locally Path1, FileList1, FileName, DirLine.
If you remember from the previous chapters, I set up a global variable to
be able to pass the User's choice: [MainDirPath]. It stores the path given by
the [Dialog.SelectDirectory()] call.
Now, we use it to list the subdirectories in this [MainDirPath].
Next, I set up an icon to be shown in the StatusBar, in case the
procedure takes longer that a few seconds. It seldom shows, but anyway, if
there is a long list of subdirectories, might show up. Looks nicer!
The Loop
As you can see, we enter a loop, where each item provided by the
[Dir(MainDirPath)] is analysed and if it is a directory, it is listed in the
[DirTextArea] control, as a new line.
Next, there is a filter there. Since I was unable to get more information
on how to use the [Dir()] command, if a directory or file belonging to Root or
eventually another user on the same computer that has restricted rights gets
on the list, the procedure triggers an "Access violation" error. As I said before,
I still have to study error handling so, I got what I could, through guess-work.
The [FMain.Refresh] call, is here to force the display of the images. I
probably forgot to change, because I later replaced it with
[StatusBar.Refresh] that seems to work better in this case. Glitches...
[ListDirAndFiles(...)]
This is the next procedure. Simpler than the first, since we list all items
found. A tweak was made here though, to make the directories easy to
distinguish from the file entries. I added a forward slash at the end, so I can
see at a glance that the item in the list IS A DIRECTORY. Otherwise, it's almost
the same procedure as the previous.
End
Although I use it seldom, sometimes I need to know all the files that are
in a certain Project's directory. I use a "Project based" approach to all my
work, so everything on the computer's storage, is organised as a Project,
having a "Main Directory" and then subdirectories and files.
[ListDirRecursively(...)]
As you see, the code is pretty simple and the only weird thing is the
recursive call, that is less transparent. The key to this is that the value of
[Path1], changes at each iteration (pass) of the loop chain. The procedure will
be released only when the last item found by [Dir(Path1)] has been listed.
If you look closely, in fact there are two loops running one inside the
other: the first loop is the one that called the procedure when the item was a
directory; the second loop, is the one that starts within the recursive call and
this mechanism goes on and on, until the maximum level of depth is reached
so if we have a 6 level tree, we'll also have a 6 level loop running, one inside
the other. This is less obvious and might lead to confusion. Using a signaling
method (the StatusBar) and a depth counter, you can check this visually, with
a global variable that gets increased at each recursive call [intLoopDepth]:
Theoretically, this is an infinite loop: the procedure calls itself and if the
exit condition, last item IS a file, somehow fails to appear, will call itself
indefinitely. Since even if there are hundreds of thousands of items, or even
millions, this is a limited (finite) amount of "something" and at some point, the
loop ends, even if that takes longer.
This is why I chose to show the current item in the [StatusBar] at the
cost of speed: the user can see if the loop actually does something.
Otherwise, it might look like frozen.
We described so far the first part of the interface and the undelying code
that does the computational part. I explained the buttons on the upper toolbar,
then the dedicated toolbar with the Radio Buttons. Below the Radio Buttons,
there is a TextArea, [DirTextArea].
We use it to build our results list.
The lines of code are almost the same line. They are different only in
what's regarding the content written in the control, which is different,
depending on what User chose to list and if the request was to list both files
and directories, the variable that contains the current item, is different:
If you check the code, you'll se the same lines in all three procedures.
The only different lines of code, are those where we add the statistics, at
the end of the list. It's done on the [ListDirBtn_Click()] event, after calling
the appropriate procedure.
Now, if you remember, we spoke about saving the results into a file.
As you can see, before hitting the [ListDirBtn] to trigger the
[ListDirBtn_Click()] event, the [DirTextArea] is empty and the
[SaveDirListBtn], is disabled (grayed).
However, when the first item is found, it is written into the
[DirTextArea], which means that its contents changed, and the
[DirTextArea_Change()] event was triggered. When this happens, the value
of the [SaveDirListBtn.Enabled] property is changed to [True]. It will
File.Save(Dialog.Path, DirTextArea.Text)
CrtListFile = Dialog.Path
Else '--- User ignored the extension; we add it.
FileExt = Dialog.Path & ".subdirfileslist"
File.Save(FileExt, DirTextArea.Text)
CrtListFile = FileExt
Endif
SaveDirListBtn.Enabled = False
PictureBox1.Picture = Picture[strIconsRelativePath & "Info-60x60.png"]
InfoBox.Text = "INFO! Directory list saved as: " & CrtListFile
FileExt = ""
FMain.Refresh
InfoBox.Text = "DirLister V " & Application.Version
FMain.Refresh
Wait 0.00001
'----------------------------- .dirlist (8)
Case "DirRecursively"
Dialog.Filter = ["*.dirlist", "Recursive Directory List"]
If Dialog.SaveFile() Then Return
FMain.Refresh
Wait 0.00001
If Right$(Dialog.Path, 8) = ".dirlist" Then
File.Save(Dialog.Path, DirTextArea.Text)
CrtListFile = Dialog.Path
Else '--- User ignored the extension; we add it.
FileExt = Dialog.Path & ".dirlist"
File.Save(FileExt, DirTextArea.Text)
CrtListFile = FileExt
Endif
SaveDirListBtn.Enabled = False
PictureBox1.Picture = Picture[strIconsRelativePath & "Info-60x60.png"]
InfoBox.Text = "INFO! Directory list saved as: " & CrtListFile
FileExt = ""
FMain.Refresh
Wait 0.00001
InfoBox.Text = "DirLister V " & Application.Version
FMain.Refresh
Wait
'----------------------------- That's it! Finish Select Case Statement...
End Select
Dialog.Filter = [""]
'====================== [SaveDirListBtn_Click()] =========================
Catch
Message.Info(Error.Text)
End
File.Save(FileExt, DirTextArea.Text)
To be able to use the same line of code, I used a variable for the
file+extension, instead of the string itself. So, the call [File.Save()] has the
"Where to save" part, the path of the file AND the desired extension, checked
before. The "What to save" part, is contained in the [DirTextArea] and we get
it from there, using the [.Text] property call.
We went through the code, following the interface shown to the user and
the last item we described, was the [SaveDirListBtn] control.
We still have some events in the same chain of events, that occure here,
in the [StatusBar]
As you can see in the screen capture, we have an image, than a text.
The [StatusBar] itself, consists of three controls. Very simple!
This is how the program "talks" to the User, while doing something that
takes a longer time than would be acceptable to wait.
As you can see studying the whole code, there is a large number of
assignments, both for images and for text displaying.
Each change, is triggered by the actions that are running or were
performed by DirLister, at some point of the workflow.
Some messages, are for fun, to get the user know that something
happened (Info icon), some others are warnings (Warning icon) some others,
just show that somekind of listing is running (Docs-Search icon). When some
kind of job has been fulfilled, sometimes the DirLister logo appears.
This is a way to add some interactivity to the program, when time
I left for the end of this book this critical part of writing, testing and
deploying an application, since it is a very general theme. It applies to all kind
of software so, it has nothing specific to DirLister or any other particular
application, written in Gambas or whatever programming language.
Designing the Help system is critical, mostly in those days since the User
needs to know "at a glance" what and how. While this might look easy at
first thought, as in "Write a PDF and you're done!", facts are a little
different.
It's all about PERCEPTION.
While this might be less obvious, the Help System is actually a
marketing tool.
It will decide largely if your application will be embraced by a larger or a
smaller number of users. And this is the critical part!
While writing an application for your own needs might be suitable for
someone who has a fortune consisting of hundreds of bank accounts filled with
countless zeroes following some "1" up to a "9" figure, for the most of us,
writing an application just for personal use, can mean only one thing: the
losses caused by its absence make the development of the app cheaper than
assuming the losses.
This is my case, by the way... I already lost about 800 (more, but it's
irrelevant) euros in storage media. Preventing it happen, cuts down my
storage media expenses with at least 65% of the above figure. That
means about 520 euros. And it took me less than 6 weeks to write and test
DirLister (regardless the documentation phase). The trick is that it cuts the
losses every year from now on. Assuming an average yearly cost of 150 euros,
the savings are almost 100 euros/year. Meaning that in the next 10 years, I'll
save 1000 euros. That's all. Figures; basic maths.
For all other reasons, marketing defines the success or failure of any
product or service. There are countless examples on the internet, I'll point you
Final words
Well, we got to the end of the first "Chain of Events". I went through
each part of the interface, the user interactions and what those interactions
change into, behind the scenes.
I hope that this approach will help you figure out how to approach your
own projects.
This time, it seems I got lucky, I managed to stick to the idea of "Short
book".
If I decide to go to the next "Chain of Events", it will be, obviously, the
next interface, [Search Lists].
A lot of stuff happens there, there are more options and more procedures
to describe so keeping it short, is impossible, unless I cripple it. Assuming a
relative degree of predictibility, the next book might have some 100 pages or
so, which has nothing to do with "short book"...
Anyway, thanks for reading and I hope this book helped you figure out
some solutions on how to use strings, and some basic controls to build a
simple application, that you can further enhance, gradually!
It is exactly the path I followed: I started from a Form, then added some
controls, tested properties, methods, events, played around with those,
combined them.
I moved then to File Operations, String Manipulation and then did
my best to gather the knowledge into a project I was interested to make it
happen: DirLister.
My best wishes and successful Gambas coding!
Şerban Stănescu