SCRIPTING OF EVENT DRIVEN PROGRAMS FOR GRAPHICAL, COMPUTER-BASED LABORATORIES Donald A. Garrett

Micheal B. O'Neal

Barry L. Kurtz

Louisiana Tech University

Keywords:

Script, GUI, Event-Driven

Introduction

At Louisiana Tech, we have been developing graphical, event-driven software for the last 18 months as part of the Watson project, an NSF sponsored project aimed at creating computer-based laboratories for introducing many of the basic concepts in computing. We consider it best to guide the students through scripted activities, yet maintain as much flexibility as possible with our labs. This paper describes a technique we have developed which allows us to impose scripts onto event-driven programs.

We have developed and implemented eight prototype Watson laboratories which are currently being used in our introductory computer science course. Activities include: applications (spreadsheets and databases), algorithms (data structures and graphics), programming languages (imperative and functional), architecture (combinational logic), and theory (finite state automata). The goal of this project is to develop a diverse collection of 10 to 12 laboratories that will make the major concepts in computing accessible to a wide variety of students and to encourage the development of critical thinking skills in these students by emphasizing problem solving in challenging, but limited, domains [OK95b] [KO94]. Information about this project can be found on-line on the World Wide Web address http://www.latech.edu/~watson.

Methodology

It was decided early on to begin by building flexible environments related to the topic being taught, and then add a layer of activity code that guides the students through specific problems. We have also added a message layer above the activities. Because of these layers, it is possible to make significant changes to the behavior of the labs with very limited changes to the actual code.

The first level, the "message level," is a keyed text file. The existence of this level allows instructors with limited programming ability to quickly change certain aspects of a laboratory exercise, such as the definition of terms, or explanation of a problem. Instructors can also add new test cases that are used to help evaluate a student's answer to a problem. Such changes do not require a recompilation of the software.

The second level, the "activity level," is a C source program that implements a script which monitors and controls the activities of the laboratory environment using a number of specialized functions implemented as part of a shared library. Instructors with a working knowledge of C programming should be able to change most aspects of an activity without needing to learn the details of the underlying software environment. The final level is the "laboratory environment level" which defines all of the software objects that make up a laboratory.

This three level approach is used by all the lab activities. We use the lab Functional Programming to illustrate these techniques in this paper. The Functional Programming lab has a general purpose Lisp-like interpreter and a general purpose syntax driven editor. This laboratory consists of a series of eight activities for students to complete. These activities describe basic concepts, allow students to solve specific problems, and provide intelligent assistance to evaluate student answers [KMO95a].

Implementing these software levels was a necessary, but nontrivial task. Essentially what the authors have done is modify a purely event driven graphical environment, SUIT (Simple User Interface Toolkit), into a much more flexible "scripted" event environment. This new environment allows Watson to present problems to a student, monitor his/her progress towards solving the problems, and even to intervene when s/he gets into trouble. Watson also takes care of many other computing details, such as loading and saving student work.

SUIT is designed as a basic programming environment for people first learning about GUI (Graphical User Interface) programming, and to be portable across the windowing systems: X-windows, Macintosh, and MS-Windows [Con92]. It does the latter by having a series of libraries for different platforms, all with a common interface. This library, Simple Raster Graphics Package (SRGP), opens a local format window as a pure bitmap, and translates all events into an SRGP format that is consistent between platforms[Skl90].

The technique described in this paper was developed using the SUIT library, but can be used with any windowing system that is event driven. Event driven is the style of programming used with almost all GUI's. In this style, each program surrenders control to the windowing system. The windowing system then makes calls to the program whenever events occur. It is different than traditional programming, because the program works by reacting to outside events and has no predetermined control flow [Pet90]. These events are usually created by user actions such as mouse clicks or keystrokes, but a wide variety of event are possible.

This paper focuses on the techniques used to build a general purpose library which supports these scripted activities and is useful for many types of laboratories. A related problem is how to save (and restore) a student's progress. In fact, we wanted the students to be able to jump between any activity without loosing their place, or just press quit at any time, and be able to come back to the same environment. This allows a student to refer back to previous answers, or to experiment using a scratch pad area, and then resume the assigned tasks at the point s/he left off.

Software Techniques

Originally all of the lab environments were strictly event driven. In order to control the actions of these labs, three key capabilities were needed.

1. A way to control which software objects (widgets) should be active at any given moment.

2. A way to save and restore the state of the lab's widgets.

3. A way to define what events are supposed to happen in what order (activity scripting).

This first need was met by an event trapper. This trapper catches all input events from the user (mouse or key clicks) and decides if they are going to an enabled or disabled widget (a unit of the program that receives events). If the events are going to a disabled widget, the events are thrown away and a beep is generated to give the user feedback. A widget may be enabled for unlimited use, limited use, or "one shot." Widgets enabled for unlimited use may be activated by the user as many times as s/he desires. Limited use objects have a preset number of times they can be used before control returns to the activity script. One shot, as the name implies, is a special type of limited use where the widget may be used only once.

One shot actions are the most common form of limited use enabling, and are normally used to give the student a way to signal that s/he is ready to continue the tutorial. Multi-use enabling is designed to help catch students that are flailing about. For example, a student is presented with a program and told to press the "run" button to observe it's behavior, and then press Ok when s/he is ready to move on. We might want to enable the "run" button for three or four activations so that the student can watch the program more than once -- however, if the user just keeps pressing "run" over and over, the activity script can assume control and try to offer assistance. There are certain system widgets that are always enabled. They allow students to do things such as pick a different activity or quit the lab.

Saving and restoring the state of the widgets is performed by allowing lab programmer's to register "set" and "inquire" functions for individual widgets. The inquire function produces a string that describes the state of a widget, and the set function accepts a string of matching format used to set the state of that widget. Only the widgets which hold the results of student work normally need these functions, and it is the lab programmer's responsibility to make sure that all needed widgets are handled. The state of a Lisp editor widget would (for example) be the Lisp function currently being edited. By combining the state strings for all widgets (and keeping track of which widget they are associated with), a global description of the lab's state can be produced. This global state can be saved or restored by calling all of the different set and inquire functions. Each lab has a neutral global start state that is loaded when the lab is first started, or whenever the user is between activities.

Developing a method for scripting activities was more difficult to implement. We began by taking each activity that students can work through, and breaking it into atomic steps. These steps are considered atomic, because a student may only resume an activity at the beginning of a step.

A function is associated with each step, and it defines the behavior of the lab during that step. Activities are then defined as an array of step functions. The movement of control between different steps of an activity is controlled by the activity level library. The code in each step can enable or disable widgets, directly control the lab, or cause the lab to "go live." Going live means that the student is allowed to do things and manipulate widgets. Control does not return to the step function until after a widget enabled for limited use expires. It is important that each step function does not block for any other purpose than waiting on the user; they should set things up for the student and go live as soon as possible. This keeps the system from freezing (from the student's point of view).

static void SampleStep(void) { tty_write("Your job is now to write a function to square a number"); EnableAll(); /* This block is normally a macro called WaitForOk */ tty_temp_write("Please press Ok button" " to continue."); Enable(OKAY_BUTTON, 1); WaitForUser(SAVE); /* End of WaitForOk macro */ }

When this step (shown above) is run, the student would see a description of what s/he is supposed to do, see instructions to press Ok, and the editor will become active. When the student presses the Ok button, which has been enabled for a single use, control will return to the step function. It will complete and control will be passed to the next step. If the student chooses to exit the environment or switches to another activity, then the global state and the number of the current step are saved. Normally, there is one saved state per student for each activity. Because everything about the environment is saved, the student can resume the activity later, at the beginning of the step where s/he left it.

There are several ways to consider implementing how to go live from inside a step function. We decided to create the macro WaitForUser. This macro calls a library function which provides an event handling loop which exits when a global flag is set. This flag is set to one value whenever a widget, enabled for limited uses, expires, and is set to a different value when the current activity is exited (by quitting or switching activities). The function containing the event handling loop then returns a value to the macro WaitForUser telling the step function either to exit immediately, or to continue with the step. This macro conditionally exits from the step function based on this value.

Because the event handler loop only exits normally when a widget expires, this is the only way that a step function can regain control. If all widgets are enabled for unlimited use, and WaitForUser is called, WaitForUser will never return control to the step function.

The WaitForUser macro is used because it minimizes the library interface, and shortens step code length. This has made the step functions as simple and readable as possible. However, despite the fact that the macro looks like a normal function call, it is a hidden exit point.

Screen Shot

Figure 1: Screen Image of Functional Lab

Detailed Example

In this laboratory (Figure 1), we have two editing windows and a variety of buttons associated with editing. The upper editing window is used to define Lisp functions, and referred to below as DEFINE_BOX. The lower window is for making calls to those functions. Because it is used for testing functions, it is referred to as the TEST_BOX. The box across the bottom of the window is the instruction window (in the code, this is refered to as the tty) and is used to display problem descriptions, and other messages to the student.

The sample sript shown in Figures 3, 4, and 5 will show a Lisp function to a student that will count the number of elements in a list along with some discussion of that function. It then assigns that student the task of writing a function that will count the number of atoms in a list and it's sub-lists. This sample assumes an interface to the glass box, and black box testing that is planned, but not yet fully implemented.

The message file, Figure 4, is stored as a textfile that can be edited to change the wording of messages or Lisp code without recompilation of the executable program. The preceding step functions, Figure 3 (explained in Figure 4), are C code and would require recompilation to change, but are much simpler to work with than the environment level code. These functions use only a small set of control functions that are the same in all laboratories, plus an even smaller set of lab specific functions. In this example, progedit_set_tree is the only function that is specific to the Functional lab.

Walk Through

A student starts the Functional lab, and identifies himself/herself. The lab initializes itself, loads the global start state, disables all widgets other than system ones, prints a message asking the student to pick an activity, and then goes live by calling the WaitForUser macro.

From the student's point of view, a window appears, s/he types in his/her name. A Lisp editor appears (empty), and s/he gets directions to pick an activity from a menu. Clicking on the editor only produces beeps, but the activity menu works normally.

When s/he chooses the activity Count, the first step of the activity is called. It loads a sample Lisp function into the editor (so the student can read it), displays a block of text describing how the function works. It then uses the WaitForOk macro which enables the Ok button for one use, and goes live.

The student now has a Lisp function pop up in the editor, and a block of text in another window. S/he presses the Ok button to move forward.

The WaitForOk returns normally, and a second block of text describing the function is displayed. This is all part of the same step.

The student gets tired of reading, and just presses Ok, so s/he can "get on with it."

/************************************/ /* Count Activity Function */ /************************************/ static void CountActivity1(void) { progedit_set_tree(DEFINE_BOX, message("Count Sample Define")); progedit_set_tree(TEST_BOX, message("Count Sample Test")); tty_message("Count Sample Explain 1"); WaitForOk(SAVE); tty_message("Count Sample Explain 2"); WaitForOk(SAVE); } static void CountActivity2(void) { progedit_set_tree(DEFINE_BOX, message("Count Skeleton Define")); progedit_set_tree(TEST_BOX, message("Count Skeleton Test")); } static void CountActivity3(void) { tty_message("Count Problem Assign"); EnableAll(); /* Turn everything on. */ do { WaitForOk(SAVE); } while (!BlackBoxTest() || !GlassBoxTest()); } /* In the same file where these */ /* steps are defined */ void InitActivities(void) { /* This array defines the */ /* activity based on the steps */ static StepFunc CountActivity[] = { CountActivity1, CountActivity2, CountActivity3, NULL }; ... other arrays for other activities ... RegisterActivity("Count", CountActivity); ... other calls to RegisterActivity for other activities ... }

Figure 2: Step functions for the Activity Count implemented in C

progedit_set_tree
This function is specific to the Functional Lab, and allows the activity level to load specific Lisp code into an editor window.
tty_message
This function prints a string from the message file to the tty window.
tty_temp_write
This prints the string as temporary highlighted message in the tty.
message
This function looks up a message name in the message file, and returns the string (possibly many lines long) associated with it.
WaitForUser
This macro causes the environment to go live. It will only return when a widget enabled for limited uses is used the final time. It is also a potential exit point for the function if the user leaves the activity. This macro can be passed one of two values. SAVE means save the global state if this step is exited at that point. NO_SAVE means don't save the state, but is rarely used.
WaitForOk
This macro enables the Ok button for a single use, then calls WaitForUser. The combination was common enough to make this macro handy.
EnableAll
The function enables all widgets for unlimited use. There are also functions for individual widgets, limited and unlimited, and for disabling widgets.
InitActivities
This function is used by the Functional Laboratory to initialize it's various activities.
RegisterActivity
This function is provided by the activity-level libraries to allow an activity to be added to the list of known activities during system initialization.

Figure 3: List of functions used in the scripting examples





* Count Sample Define (define count (lst) (cond ((null lst) 0) (t (+ 1 (count (cdr lst)))) ) ) * Count Sample Test (count '(a (b c) d)) * Count Sample Explain 1 This function counts all of the elements in a list. Both atoms and lists will be counted as a single element. The above test case will return a value of 3. * Count Sample Explain 2 This function uses a technique called recursion (covered in your book). It counts the elements in a list by finding out how many elements are in the list with the first element removed, and adding one to that number. * Count Skeleton Define (define CountAtom (lst) (cond (<test> <result>) (t <result>) ) ) * Count Skeleton Test (CountAtom '(a (b c) d)) * Count Problem Assign Your new assignment is to write a function that will count the number of atoms in a list. This includes all atoms in sub-lists. The correct result for the sample call is 4. You may assume that the parameter is always a li

Figure 4: Selected entries from the Functional message file for the activity Count

That WaitForOk was the last thing in the step, so the step function returns. The global state is then written out (a precaution against a system crash), storing the fact that this student has finished the first step in activity Count and the state of all widgets when s/he did. In this case we don't care much about the state of the widgets, but later on we will.

Step 2 of Count is now called. It loads a partially completed Lisp function into the editor for the student to finish and exits immediately. The state is saved, including the partially finished function. This step could have been merged into the end of step 1, but it's a little more readable this way. The reason for having step 2 separate from step 3 will become apparent below.

Step 3 begins and displays a message telling the student what the unfinished function is supposed to do. It enables the editor widget for unlimited use, and the Ok button for one use. Instructions say to press Ok when the student thinks the function is correct. The editor includes a button to run the function, so the student can test his/her function before pressing ok.

This is the first thing the student has seen since s/he pressed ok to "get on with it", and s/he immediately wishes s/he had read the text s/he skipped explaining how this Lisp function is supposed to work. At this point the student can restart the current activity by pressing the "reset" button or decide to move on to some other activity. Our student uses the editor, trying to solve the problem, but doesn't meet much success. S/he decides to back up to a previous activity "Function" that s/he had skipped early on (the one that teaches what a Lisp function is), so s/he picks the activity Function from the activity menu.

Step 3 had no control while s/he was editing, so hasn't done anything since going live. When the student decides to switch activities, the step function is immediately forced to exit. The global state is saved, including all the editing work that has been done so far, and the fact that s/he was in the middle of step 3.

The student works his/hers way through the Function activity from beginning to end, and gets an idea about how to solve the current problem. S/he then decides to return to the Count activity.

When s/he does, the global state for that student in activity Count is reloaded, and control is returned to the beginning of the function for Step 3. The same message describing what the student should try to accomplish is again displayed, the editor is again enabled, and s/he is again allowed to work, but with his/her old editing changes still present.

In this example, testing is performed to find out if the student's answer is correct. If it is not, the student is informed of the reasons for failure (by the testing mechanism) and is given the chance to try again.

When the student does successfully finish the step, the final step of the activity exits, and a message is displayed telling the student that s/he has finished the activity.

Conclusions

What has been outlined here is a description of a system that has been implemented to allow students to pursue their goals in open environements, but with the supervision of the tutorial. By using our assistant to detect student errors, and careful design of activities to fully introduce new material, these techniques allow students to receive immediate feedback, and continual guiadence while exploring the subjects covered by labs. This means that the learning experience is being improved while the workload for teachers is reduced.

The authors can be reached at the following email addresses:

Donald A. Garrett

dgarrett@engr.latech.edu

Micheal B. O'Neal

mike@engr.latech.edu

Barry L. Kurtz

kurtz@engr.latech.edu


Reference

[KO94] Barry L. Kurtz and Micheal B. O'Neal. A software laboratory environment for computer-based problem solving. National Educational Computer Conference, 1994.

[KMO95a] Barry L. Kurtz, Unmesh S. Mayekar, and Michael B. O'Neal.. Design and implementation of a generalized problem solving assistant for algorithm development. SIGCSE, 1995.

[OK95b] Michael B. O'Neal and Barry L. Kurtz. Watson: A modular software environment for introductory computer science education. SIGCSE, 1995.

[Con92] M. J. Conway. The SUIT Version 2.3 Reference Manual, 1992. inquiries can be sent to suit@uvacs.cs.virginia.edu.

[Pet90] Charles Petzold. Programming Windows. Microsoft Press, 2nd edition, 1990.

[Skl90] David Frederick Sklar. SRGP for ANSI C, 1990. further information at ftp://wilma.cs.brown.edu/pub.


garrett@bgb-consulting.com

©Me, Myself, and I!