To order Alpha Four or Alpha Five, click above to go directly to Alpha, or call 800.451.1018 or 781.229.4500

             
HOME
TOPICS
News & Notes
Learn Alpha Five
Tips & Tricks
Tech Corner
Developer Spotlight
CONTENTS
Welcome
Alpha Five Developer's Conference
Alpha Five to the Rescue
Building an Application
Interview with Steve Workings
Quick "Find" button.
Alpha Four to Alpha Five transition
"Hot" keys for non-mouse users
AlphaHolic Anonymous
About the Editors...
Alpha Five to the Rescue
by Jim Chapman

This article is not intended to delve into the intricacies of creating a polished application. My intent is to show the flexibility of Alpha Five and how with a little creativity you can solve every-day problems. A simple Xbasic script and a couple of tables is all it takes to make this work. In my earlier article about creating a similar application in Alpha Four I boasted about how quick it was to do this with Alpha Four, and it was. However, I have to say, I think it is easier to do in Alpha Five (maybe I’m just older now.. )

Several years ago I wrote an article, Alpha Four to the Rescue, that was published in the Alpha Forum magazine. It told of a situation that developed when I had suddenly learned I was responsible for a road race and walk during a summer celebration in my town. Prior to this the road race had been handled by a different organization, but because of poor communication, it was just two days before the race and I discovered I was in charge. It came down to a question of how many volunteer helpers I could get in a short time. One aspect of the race that was fairly labor intensive was the timing of the race. The race had been run with a group of timers, all equipped with stop watches and several helpers to record the times and several helpers to take the race tags off the runners and walkers, and categorize them. Not only did I not have the time to round up all the helpers that would normally be needed, but I did not have access to all the stop watches I’d need. With time growing short, I made a decision that my wife and I along would have to handle the finish line. I sat down with Alpha Four, which I used exclusively at the time, and in a very short time hammered out a race timing application. It was so successful that I have remained in charge of timing the race ever since.

That brings us up to the present. Several years ago, struggling and kicking, I made the switch from Alpha Four to Alpha Five. Not looking back, I have developed exclusively in Alpha Five ever since. Once a year the road race would roll around and I would drag out my old laptop with the Alpha Four race application and time, correlate, and print out the race results. Over the years I made several improvements to the user interface of the application, making it easier and quicker to use. This last year my son was going on a business trip and needed a laptop for a short period of time. Never thinking twice, I loaned him my laptop and off he went.. so did my Alpha Four race application. With the proper application of Murphy’s Law, my laptop had a hard drive crash. I had backed up the hard drive in years past, but the last few years worth of upgrades to my race application died with the hard drive. My life is busy and as the race approached, I struggled with whether to, use the old application without the improvements, try to remember enough Alpha Four to recreate the improvements, or just redo it in Alpha Five. I decided to use the old application without the improvements. A couple of days before the race I found myself poking around in Alpha Four trying to remember what I had to do to run the application without the aide of the user interface that was lost. Frustrated, (it’s amazing how quickly you forget things that you no longer use), at the last minute I decided to see if I could create the functionality in Alpha Five in the short time remaining before the race was to be run. I was amazed at how quick and easy it was. 1

The application is quite simple. It consists of three tables, a script, and a report. The tables are 1)Runners, where data about the participant, such as name and address, is stored, 2) Timing, where the elapsed time of the runners is stored, and 3)Category, a table to store the different categories of the race. In my case we have a 1K walk, a 1K run, a 5K walk and a 5K run all going on at the same time. There is a field in the Runners table called ‘Runner_Numb’. The runner’s number is a unique number that is assigned to a runner. This number is printed on a runners tag, a piece of paper pinned to each runner’s jersey. With these simple structures in place, the race is ready to begin.

Before the race starts, I zap (empty the table) all the records in the Timing table. Then I open up the Timing form. When the starter’s gun goes off, the 'Begin Timing' button is pushed. That runs a short embedded script that sets the current time at the start of the race. The heart of this application is a script that calculates the elapsed time of each runner or walker as they cross the finish line. This script is embedded in the OnKey event of the form. As the race progresses, the form waits for further input via the onKey() event. As runners cross the finish line I press the space bar for each runner. When the space bar is pressed the script calculates the elapsed time between the time the race was started and the time the space bar was pressed. A new record is then written to the timing table that contains this elapsed time.

As people cross the finish line, my wife is waiting for them with a fish stringer (basically a piece of string with a big knot at one end). The runners tag, pinned to each runners jersey, has a tear off portion. On the tear off portion the runner's number is printed. My wife takes this tear-off and strings it onto the fish stringer in the order the runners cross the finish line. After the last runner has crossed the finish line, I close the timing form and open up the default browse of the timing table. The table is simple, just 3 fields, a time field, a place field, and a runner_numb field. My wife reads off the runners' numbers in the order they crossed the finish line. Beginning with the first record, in record number order, I place the runner’s number in the field. This is very quick as I just ‘Down Arrow’ through the browse. Once this is done a one-to-one set correlates the Time table with the appropriate runner’s record. A report is then printed out that shows the placing of the runners in their various divisions as well as overall.

The script that does the timing, (actually it’s the only script so far except for the simple one embedded in the Begin Timing button!) is attached to the OnKey() event of the Timing form. It is a short and simple script and is reproduced here:

if a_user.key.value = "{space}"
    a_user.key.handled = .t.
    if a_user.key.event = "up"
       t = table.current()
       select
          case t.mode_get() = 0
          t.enter_begin()
          case t.mode_get() = 1
          t.change_end()
          t.enter_begin()
       end select
       t.time = str((now() - var->vbegintime)/60,8,4)
       var->vplace = var->vplace + 1
       t.place = var->vplace
       t.enter_end()
       var->currenttime = t.time
   end if
else
   a_user.key.handled = .t.
   if a_user.key.event = "up"
      ui_msg_box("Ooops","You didn't hit the right key!")
   end if
end if
parentform:currenttime.refresh()
parentform:vplace.refresh()
end

First, be aware that in the form properties, restrictions need to be selected for ‘Enter Record’, ‘Change Record’, and ‘Delete Record’. You don’t want to make any changes to the underlying table from this form. 2

If you’ve never used the OnKey form event, this script will be a good place to start experimenting. Before we investigate the script I’d like to point out what I believe to be a quirk in the OnKey() form event. I wanted to use the space bar to trigger the OnKey script. It is larger than the other keys and easy to hit. In this particular script, when you press the space bar, the OnKey 'down' event fires, but the a_user.key.value (a built in global variable) is blank, when it should contain the value of the keystroke, in this case '{space}'. I would have preferred to use the down event to time the runner, as this is the first event to fire, but because of this anomaly I had to use the ‘up’ event instead. This probably isn’t an issue with the speed of today’s computers, probably not an issue with yesterday’s either. If you’d like to check this out yourself, put a ‘debug(1)’ command at the beginning of the script, enter the ‘a_user.key,value’ and ‘a_user.key.event’ in the watch window and see what happens. A short explanation of the script follows: 3

if a_user.key.value = "{space}"
The first line of the script checks to see if the space bar was pushed. If the space bar was not pushed then the script execution jumps down to the ‘else’ portion of the script. If it was a space bar keystroke, then the script continues execution with the next line.

a_user.key.handled = .t.
If the space bar was pushed, then this line of the script ‘absorbs’ the keystroke. In other words, it’s thrown away.

if a_user.key.event = "up"
There are three events possible in a keystroke, down, repeat, and up. As I mentioned earlier, the down event was not usable in this particular setup, and as for every ‘down’ there has to be an ‘up’, the up event is what I use to write the record to the Timing table. If the keystroke is 'space}' (the space bar was pushed) and the event is 'up', this the following section executes and writes a record.

t = table.current()
I get a pointer to the current table underlying the form, which in this case is the Timing table.

select
case t.mode_get() = 0
   t.enter_begin()
   case t.mode_get() = 1
   t.change_end()
   t.enter_begin()
end select


In this section I check to see what mode the table is in. The *.mode_get() method returns a 0 (zero) if the table is in view mode (no enter or change of a record is in process), a 1 if the table is in change mode, and 2 if the table is in enter mode. Because I have restricted the forms properties to not allow the entering of a new record, the changing of an existing record or the deletion of a record, the mode of the table should always be 'View' or 0 (zero). But you always need to be a little paranoid and try to anticipate the unexpected. If the table is in view mode, I put it into enter mode with the first case statement. If the table is in change mode, the second case statement ends the change mode (saving the change if it is in progress), and puts the table into enter mode. If the table is already in enter mode, nothing needs done, so there isn’t a case statement to that effect.

t.time = str((now() - var->vbegintime)/60,8,4)
This expression takes current time and subtracts it from the beginning time of the race. The value is in minutes and is in decimal format. This value is placed in the Time field of the Timing table. I probably should change it to seconds either here or at the report. But I’ll leave that for you to do. var->vplace = var->vplace + 1
This expression sets a global variable to the place of the runner in the overall race. The first runner to cross the finish line is 1st, the 2nd...well you get the picture.

t.place = var->vplace
The runners place is placed in the Place field in the Timing table.

t.enter_end()
The new record with the runner’s elapsed time and place is saved to the table. var->currenttime = t.time
This line sets a variable to the elapsed time of the last runner to cross the finish line. This is displayed to the user so they can call out the times if they want.

end if
Ends the if statement which was executed if the space bar was pushed and the event was 'up'.

Else
The else part of the if statement executes if another key besides the space bar was pushed, or if the space bar was pushed but the event is either down or repeat. Execution jumps to here and throws away the unwanted keystrokes.

a_user.key.handled = .t.
If any of the above conditions are met, we want the script to throw away the keystroke, and this statement does just that.

if a_user.key.event = "up"
This section is just for my sanity. During the time between the start of the race and the time the first runner crosses the finish line, I begin to get very paranoid. I sit there and think about all the things that might go wrong or that already have gone wrong. How do I know that the computer hasn’t crashed and it’s just showing me a frozen screen with my timing form on it?

ui_msg_box("Ooops","You didn't hit the right key!")
The relieves me. If another key it pushed and the OnKey event is 'up', show me a message and let me know the computer is still alive and well.

end if
End the last if statement that brought me some sanity.

end if
Ends the if statement that began the script.

parentform:currenttime.refresh()
parentform:vplace.refresh()

These two statements just refresh the display showing the user the current elapsed time and place of the runner.

End
The end of the script.

Simply put, this script waits for a keystroke. If the keystroke is the space bar, the script subtracts the time the race started from the current time. It then creates a new record in the Timing table and saves this elapsed time in this record. As the records are of course saved sequentially, then the first record will contain the elapsed time for the first participate that crossed the finish line. My wife places this runners tag number on the fishing stringer. For the second runner to cross the finish line, my script creates the second record with the second runners elapsed time. My wife takes the second runners tag and places it on the fish stringer, where it is of course, the second tag number on the line. And on it goes. It works well. In fact we’ve done it now for a number of years and the computer has never been blamed for a mistake. The files are available in the Code Archive on Alpha Software's Message board, Code Archive , for download if you’d like to look at them. As I’ve stated earlier, this is not a polished app with all the bells and whistles attached. It doesn’t even have a menu form (see the article in this issue on applications). It works for me and does the job I ask of it. I’d like to hear from you if you have an insight as to why the space bar keystroke does not appear in the a_user.key.value during the 'down' event. If anyone out there has an interest, why not take these files, polish this up and let us know how this works for you and what neat functionality you’ve added. Might mean we’d have to publish a second installment of this ?

Foot Notes:

1 A quick disclaimer here, this is not something that you'd try to time the Boston Marathon with. Windows is not a real time operating system. Windows has a mind of its own, and may, based on your configuration, decide at any given time to 'go' do something else for a while. But, by making sure that all background processes were shut down, I doubt that you'd have a problem with this issue. Another limitation is in timing a group of individuals that cross the finish line simultaneously, or nearly so. The system times as fast as you can press a key, but when multiple people cross the line together their times will increment only as fast as your finger can press. In the majority of recreational races, this also shouldn't be a problem.

2 Normally I would not have used a form based on the table that I was writing records to, I would have created a form based on a dummy table. But in this case I was concerned about making sure the script ran as fast as possible. If I had based the timing form on a dummy table, then for each runner to cross the finish line, I would have had to open and close a new instance of the table (or have an open 'hidden' form based on the timing table). In Alpha Five version 4.5 and prior, you cannot have a persistent table pointer across multiple instances of a running script. In version 5 this can be done. By building the Timing form based on the Timing table I can use the Table.current() method rather than opening a new instance of the table.

3 I did not dim (dimension) the variables in the script. These variables are dim'ed as global variables initialized during the onPush event of the 'Begin Timing' button on the Timing form. I didn't reference them in the script because it isn't strictly necessary and I did not want to waste the time executing these statements every time the script ran.
[PRINTER FRIENDLY VERSION]

LETTERS

There are no letters for this article. To post your own letter, click Post Letter.

[POST LETTER]
Powered by iMakeNews.com