This is the second of a three part tutorial concerning making a program which uses the IO functionalities of SIMPOL to create an application which will take as an input a specifically formatted text file and return how much time was spent working on a specific task, in the first part we created a simple application that would do this, but this would either need to be launched from the IDE or using a complicated command, neither of which made a pleasant user experience. In this second part of the tutorial we are going to create a UI program that will allow us to find the file we which to use and then execute the program we previously created.
There are two ways to make a UI element in SIMPOl, using the Superbase NG Personal Form Editor and then adding functions to the form code given by Superbase NG Personal. It is also, of course, possible to write the entire program yourself, but this is often unneccessary and will take considerably longer to achieve the same results. We will be taking the first approach and creating the form first in the Form Editor, saving it as a program that can be edited, and inserting the neccesary code to make it execute how we want it to.
This is an intermediate use of the wxWidget component, for a more basic introduction there is a chapter in the Programmer’s Guide dedicated to this (found here as well as included in your installation) as well as a basic introduction to Dialog style programs (found here). This program will not go into significant depth into the wxWidget library, but it is often useful to have a basic understanding of what you are doing, it is thus highly encouraged that you read through at least one of these, if not both.
Creating our Project
Before we can start designing our program using the form designer it is a good idea to create a blank project, this will give us a place to save the form files that we are going to make using Superbase NG Personal
Once we have created our new project we need to activate the wxWidgets components, this can be found in Project Settings in the SIMPOL components menu (seen below)
That is all we are doing in this for now although we will need to insert more libraries later on in the project
Creating the Form in NG Personal
Once you have launched the Superbase NG Personal you will be met with the following screen
From here you want to create a new form (
New -> Form...) . This will create a new window with a page on it, you should at this point save the form something sensible.
N.B. It is not neccessary to save forms
.sxf files in the same folder as your project as we will later need to export it as a
.sma to use it in the program anyway. Futhermore, regarding naming your files, the best practice is to differentiate the project and the form file.
Once you have saved the form something sensible you want to change the size of the page, in the example provided we have made the window a 450 wide by 110 tall box, this is of course up to personal preference and taste. To access the Form and Page Properties window, simply double click a blank part of the page. The following dialog should pop up here you can change the Form and Page Name as well as the colour of the form, although it is best to use a System Colour for more cross-platform consistency
Once we have done this we can start to draw things onto the page, the end result should look something like this:
The version you will create has a different background colour, but that is just a slight cosmetic change
The first thing we will be adding to our page is the text that reads “File”, this is very easy just select “Draw a label” and draw it roughly where you want to position it. This will open the Text Properties dialog (below) now all you need to do is write your text you want to display in the Text box as well as change the name to lFile (more about the control naming convention here)
Your window should now look something like this
This is clearly not right, the text has been coloured incorrectly, to correct this double click on the text you just made to bring up the Text Properties dialog again, to correct the off colour tick “Use System Colors” and then change the background colour to “
Once this is done the text should now look consistent with the rest of the window.
Next we need to add the Editable Text Box, two buttons for OK and Cancel as well as the
Once this is done we can add the Editable Text box, the exact positioning and sizing does not matter yet as we are going to be resizing and aligning these later anyway
The next thing we want to add is three buttons: two text buttons for OK and Cancel (call these bOK and bCancel) as well as a bitmap button to which we will add an icon (call this bbFileSelect). Previously we have not needed the user to interact with what we have created, however for the buttons we want an action to occur when the end user clicks on the button. This is where Events come in, these are how SIMPOL allows a user to interact with UI, when an Event is triggered (such as when a user clicks on a button) that can call a function, this allows us to act upon a users interactions with the form. We will only be using onclick for now but for each of the buttons we want to create a sensible function name.
N.B. It is good practice here to create function names which describe what the object being intereact with is and what action has occurred. For example we will name our onclick function for our OK button “
Now you can fill in the rest of the buttons as follows
For the file selection button the bitmap files can be found under C:\SIMPOL\resources\bitmaps, we are using the 16×16 save icon, and 16×16 save_disabled pictures respectively.
At this point we are ready to finalise the interface for this GUI, this is personal preference, however several things should be considered:
- Ensure the OK and Cancel buttons are the same size as well as horizontally aligned with each other
- The Editable text box should be large enough to contain the whole file name
- The two text boxes and the file open button should all be aligned horizontally
At this point your version should look similar to the one shown above, although evidently this is also down to taste
The last thing we need to do in Personal is save this as usable code, to do so go to
File > Save As... > wxform Program... and save it in the project space we created right at the beginning
Making the form run
Adding Event functions
To make a graphical program run you will first need to add all the event functions we’ve added to the buttons. For now we will not flesh these functions out as it is not yet neccessary. To access the Form program we’ve saved within our new project we will need to include this file.
You should at this point copy over the file we created in the last part into the same location as the form file (if you do not have it on hand, there is a download of everything at the bottom of the page). Now the following code is required:
include "AdvancedIOForm.sma" include "AdvancedIO.sma" function main() end function ""
Now if you save this project the Project Tree should look like the one below (the names of your individual files may vary)
If you don’t see the
uisyshelphdr.smathis could be because in the form the include statement has not been included, if this is the case add the following to the top of the form program
Again save the file, the project tree should now look like the above image.
Once this is done we can get down to actually getting the form to run when you execute the program. This is a two part process, the first of these is completing the form program, and the second is the actual code to create a window and set the form to that window.
We will start by simply adding the event functions. The code for that is very simple
function fileselectbtn_oc(wxformbitmapbutton me) end function sFilename function closebtn_oc(wxformbutton me) end function function okbtn_oc(wxformbutton me) end function function errormsg(string s) end function
This code will not do anything when an Event occurs but it will allow us to see the form. If you press execute (CTRL+E) the program will fail as we have not yet assigned the form to a window. That is what we are doing now
Creating a window
In the main function you created earlier we will now be creating a dialog and initialising the form we have just made. To do so we first need to declare the following variables
wxform f wxdialog d integer iError
We will then need to initialise these three variables, the first is
ierror = 0
We then need to initialise the form we have just made, first doublecheck the name of the function which contains the form, in our case its called IOGUI, and then you need to refer the variable f to the function as follows
f =@ IOGUI(error = iError)
This means whenever we use f SIMPOL knows we are essentially doing a function call to the form. At this point we want to check if this has worked properly, if it has we want to then create a window and assign the form to that window, otherwise we want to create some sort of error dialog.
if f =@= .nul errormsg(iError) else d =@ wxdialog.new(1, 1, innerwidth=f.width, innerheight=f.height, visible=.false, captiontext="Advanced IO GUI Window", error=iError) if d =@= .nul errormsg(iError) else f.setcontainer(d) //centerdialogonparent(d) d.processmodal(.inf) end if end if
This is slightly more complex but what is happening here is we check if
f does not refer to a null object, if it does that means it was not initialised properly and thus an error message displaying the error code should come up. Otherwise we are creating a new dialog with the same width and height of the form whose caption is “Advanced IO GUI Window” although you can change this.
At this point we also check that this was initialised properly in the same way we just did with the form. Once both of these have been tested we set the forms container to the dialog we just created and then we make the dialog window visible using the
processmodal function, setting the timeout to
.inf which will ensure that unless the user quits the client the window will continue to be visible.
Finally, before we can execute the program we need change one more thing. Since we have merely copied over our program from the previous part we will need to change the name of the main function, this is because otherwise this program will have two main functions and will accordingly throw an error. Once you rename the function to something sensible,
IO, is fine the program will now execute and the form should show up
Finishing the Event functions
It now seems like a good time to make some of the buttons function, we’ll start by making sure that the Cancel button closes the window. The function consists of a single function
function closebtn_oc(wxformbutton me) me.form.container.setvisible(.false) end function
This function is simple but it introduces a new very important concept recursion, in the sense that this function calls itself to get to the function that sets the form which contains the button to visible, in our case invisible.
The next function we need to create we have already referenced twice in the creation of our window,
errormsg, that function looks something like this
function errormsg(string s) wxmessagedialog(message=s, captiontext="SIMPOL Color Lab Error", style="ok", icon="error") end function
This function is the simplest one we will be creating here, it calls wxmessagedialog which creates a message box dialog with the error code in it and an OK button to dismiss the dialog.
An important concept which we need to talk about before we can create the next to functions is the member operator (!) this allows us to using the name of the button reference to it, in our case we will be using this with our editable text box but the same concept applies. The first of the two functions that requires this is bitmap button
function fileselectbtn_oc(wxformbitmapbutton me) string sFilename, sResult, cd cd = getcurrentdirectory() sFilename = "" sResult = "" wxfiledialog(.nul,"Open File for Analysis",cd,"", "Text File (*.txt)|*.txt","open,mustexist",sFilename,sResult) if sResult == "ok" me.form!tbFileSelect.settext(sFilename) end if end function sFilename
This function is more complex, it calls a file dialog which will then if the user has succesfully selected a file change
"ok" and when this occurs we will using the member operator change the text in the editable text box to the file location. This will later be used as the input for the program we created in the previous part
Running the IO Program
We have only got one function left to write, that is the function that when you click on OK will attempt to take the input file we have just specified and use it as the input for the IO program we wrote in the first part of this tutorial. We renamed the
main function of our previous code and will now attempt to execute this code.
function okbtn_oc(wxformbutton me) string file file = me.form!tbFileSelect.text IO(file) me.form.container.setvisible(.false) end function
Again this function shows off member operators and recursion. We declare a string variable,
file, which we then set to the text of our editable text box. This should ideally be the location of the text file the user wants to input into the IO function. The next thing we do is call the IO function which will create a text document in the project file containing the split daily times and total time, as it should from the previous part of this tutorial. Finally we are then setting the form to invisible, thus closing the application.
The final part in this tutorial will add more functionality to this program, we will add a window which will display various aspects of the output we are currently getting as a text file to allow for an easier and more complete user experience. Part 3 can be found here, Part 1 here
include "AdvancedIOForm.sma" include "AdvancedIO.sma" function main() //Declaration wxform f wxdialog d integer iError //Initialisation iError = 0 f =@ IOGUI(error = iError) if f =@= .nul errormsg(iError) else d =@ wxdialog.new(1, 1, innerwidth=f.width, innerheight=f.height, visible=.false, captiontext="Advanced IO GUI Window", error=iError) if d =@= .nul errormsg(iError) else f.setcontainer(d) //centerdialogonparent(d) d.processmodal(.inf) end if end if end function ""
This is the same as before except for the function name
constant sTIMEFORMAT "0h:mm:ss" function IO(string sInputfilename)
// REDACTED - Automatically generated form program file // Generated by SIMPOL form library // Requires uisyshelp.sml include "uisyshelphdr.sma" function IOGUI(integer error) wxfont font1 wxform f type(wxformcontrol) fc integer e, dpix, dpiy number factor wxbitmap bmp syscolors colors sysrgb clrWindow sysrgb clrWindowText sysrgb clrBtnFace sysrgb clrBtnText string sFilename, sResult, cd sFilename = "" sResult = "" cd = getcurrentdirectory() colors =@ syscolors.new() clrWindow =@ colors.getsyscolor(COLOR_WINDOW) clrWindowText =@ colors.getsyscolor(COLOR_WINDOWTEXT) clrBtnFace =@ colors.getsyscolor(COLOR_BTNFACE) clrBtnText =@ colors.getsyscolor(COLOR_BTNTEXT) font1 =@ wxfont.new(facename="MS Shell Dlg 2", pointsize=8, style="n", weight="n") e = 0 dpix = 0; dpiy = 0 getdpivalues(dpix, dpiy) factor = dpix / 96 f =@ wxform.new(width=450 * factor, height=110 * factor, error=e) if f =@= .nul error = e else f.setbackgroundrgb(clrBtnFace.value) fc =@ f.addcontrol(wxformtext, 16 * factor, 24 * factor, 34 * factor, 14 * factor, \ "File", name="lFile", alignment="left", error=e) if fc !@= .nul fc.setbackgroundrgb(0xf0f0f0) fc.settextrgb(0x0) fc.setfont(font1) end if fc =@ f.addcontrol(wxformedittext, 52 * factor, 20 * factor, 341 * factor, 23 * factor, name="tbFileSelect", editstyle="readonly", text="Select Text File",alignment="left", error=e) if fc !@= .nul fc.setbackgroundrgb(clrWindow.value) fc.settextrgb(clrWindowText.value) fc.setfont(font1) end if bmp =@ wxbitmap.new("C:\SIMPOL\resources\bitmaps\16x16_save.png", format="png") fc =@ f.addcontrol(wxformbitmapbutton, 404 * factor, 17 * factor, 29, 28, bitmap=bmp, name="bbFileSelect", tooltip="Select a File", error=e) if fc !@= .nul bmp =@ wxbitmap.new("C:\SIMPOL\resources\bitmaps\16x16_save.png", format="png") fc.setbitmaps(selectedbitmap=bmp) bmp =@ wxbitmap.new("C:\SIMPOL\resources\bitmaps\16x16_save_disabled.png", format="png") fc.setbitmaps(disabledbitmap=bmp) bmp =@ wxbitmap.new("C:\SIMPOL\resources\bitmaps\16x16_save.png", format="png") fc.setbitmaps(focusbitmap=bmp) fc.onclick.function =@ fileselectbtn_oc fc.setbackgroundrgb(clrBtnFace.value) end if fc =@ f.addcontrol(wxformbutton, 100 * factor, 62 * factor, 111 * factor, 26 * factor, "OK", name="bOK", error=e) if fc !@= .nul fc.onclick.function =@ okbtn_oc fc.setbackgroundrgb(clrBtnFace.value) fc.settextrgb(clrBtnText.value) fc.setfont(font1) end if fc =@ f.addcontrol(wxformbutton, 241 * factor, 62 * factor, 111 * factor, 26 * factor, "Cancel", name="bCancel", error=e) if fc !@= .nul fc.onclick.function =@ closebtn_oc fc.setbackgroundrgb(clrBtnFace.value) fc.settextrgb(clrBtnText.value) fc.setfont(font1) end if end if end function f function fileselectbtn_oc(wxformbitmapbutton me) string sFilename, sResult, cd cd = getcurrentdirectory() sFilename = "" sResult = "" //wxfiledialog bug wxfiledialog(.nul,"Open File for Analysis",cd,"", "Text File (*.txt)|*.txt","open,mustexist",sFilename,sResult) if sResult == "ok" me.form!tbFileSelect.settext(sFilename) end if end function sFilename //Close working function closebtn_oc(wxformbutton me) me.form.container.setvisible(.false) end function function okbtn_oc(wxformbutton me) string file file = me.form!tbFileSelect.text IO(file) me.form.container.setvisible(.false) //figure out how member operator would work end function function errormsg(string s) wxmessagedialog(message=s, captiontext="SIMPOL Color Lab Error", style="ok", icon="error") end function