The TaskTest Library.
The purpose of this Library is to set up a series of blocks of user interface tasks, suitable for psychological/user interface timing tests. Nominally, there is support for 2 interface tool kits -- GLUT and Xt. However, GLUT does not work very well for this type of work, so it has been abandoned in mid-stream. Many features in the Xt version are not present in the GLUT version.
There are three types of people in the experimental regime: The programmer creates the user interface. The experimenter runs the user interface program and sets up the sequencing of experimental tasks. The user (experimental subject) uses the program and performs the tasks that the program tells the user to do.
The TaskTest Library (referred to henceforth as TaskTest) organizes a series of independent interactions with the user. Each interaction, or trial, asks the user to perform one simple task, and records the amount of time that it takes the user to do the task. As each task is completed, the timing data for that trial is appended to a file called LOG.txt. The list of tasks to perform is contained in a file called tasks.list. Upon program initialization, tasks.list is read, and the content of this file determines what tasks the user will perform. It is anticipated that the experimenter will use tasks.list to set up different experimental strategies. The programmer simply sets up the program so that the user can perform any prompted task.
To write a program that collects user data, the programmer must first write an Xt program that provides the interaction techniques to be tested. Then the programmer must call TaskTest setup routines that read tasks.list and start task sequencing. Assuming that the experimenter is interested just in task times, the programmer must also instrument the completion of each interaction technique of interest. If interim times needed (first button hit, passage from menu to submenu, etc), then these can be instrumented also, but TaskTest does not handle this yet.
The tasks.list file contains a list of blocks of trials. Each block may contain one or more trials to be repeated one or more times. Each block is executed once in sequence, so looping over tasks is accomplished only within each block.
The grammar of tasks.list is quite simple. Each line contains either one command with a single parameter, or a comment. A leading # delineates a comment. There are five commands, as follows:
This indicates the start of a block of trials. The key word block is followed by some text that will be shown to the user to illustrate what should be done in the following block of trials. This description text may contain spaces, but no special characters, as they will be printed verbatim on a popup prompt window.
The block key word is the start of a new block. Any previous block is completed when a new block is encountered. A block definition continues until the next block key word occurs, or until the end of file.
This key word indicates the number of seconds of rest that the user should get between trials. restdelay is followed by a single int lying in the range 1..15. The current restdelay parameter remains in effect until the next restdelay parameter is encountered, and need only be set up once at the start of the file.
Within a block, multiple passes are made through the entire set of tasks. All tasks are tried once before a task is repeated. First pass presents the tasks in random order. A latin square controls task order for subsequent passes, so as tasks repeat, they will not be in the same order.
The taskpasses keyword indicates the number of passes that should take place through the tasks in the block. taskpasses is followed by a single int that must be greater than 1. By default, the number of taskpasses is equal to the number of tasks, and this parameter can only be used to reduce the number of passes.
This key word indicates the name of the menu that should be used in this block. Following this key word is a single string parameter without spaces that is the name to be used. TaskTest looks this name up in an internal database and calls a callback routine that initialize the named menu. This database must be set up in advance by the programmer as part of the TaskTest initialization procedure. During the initialization process, the programmer must declare each menu name and its associated callback function to TaskTest. When a new block is entered, TaskTest calls the callback associated with the menuname parameter, so that the application can prepare the correct menu.
Any line that begins with an integer is a line indicating a task that is within the current block. Each task must have a unique integer TaskID. A prompt string indicating the task name must follow each TaskID. This prompt string will be popped up onto the screen to prompt the user, and may contain spaces and so on. When the prompt pops up, a countdown will start. The experimenter should tell the user to start the interaction when the countdown completes and the popup box disappears. When the appropriate menu has been selected, TaskTest uses the TaskID to verify that correct menu item was selected.
Programming with TaskTest
To use TaskTest, the programmer must set up an X Windows environment. The basic structure is to repeatedly have TaskTest determine the next task, pop up a prompt window to tell the user what to do, then pop down the window and start a timer. When the timer is started, regular application-level interaction with menus is enabled, and when the user has selected an item, the callback for the selected item notifies TaskTest that the interaction task is complete. When TaskTest is notified, it stops the timer, logs the time (and error, if necessary), and prompts for the next task. Control thus passes from TaskTest to the program, and back again. TaskTest is in control while the popup prompt is visible, and control is given to the application program when the prompt window is taken down and the timer is started.
The basic structure is to initialize an X Windows program by setting up widgets, and to instrument each widget callback with a call TTnotify_task(TaskID)
When a menu is defined during widget setup, a call should be made to TTAddEnableCallback( callback, name, pointer ) to enter the menu's name and data structure pointer (e.g. Widget) in the TaskTest's list.
This allows extra flexibility in creating multiple blocks of trials, with each block having a different menu. When a new block starts, the application program must prepare the associated menu, and TaskTest does this by calling the callback registered by TTAddEnableCallback (). TaskTest determines the menu name from the MENUNAME registered in the current block in tasks.list.
When TaskTest starts a new block, this registered callback will be called so that the user program can set the previously-entered widget as the one to be enabled on user button hits. The application programmer must supply the callback function. It is not a TaskTest function.
Also in menu widget setup, the appropriate event handler should be enabled. One important feature is to disable user button hits while the user is being prompted. Checking the global variable TTTaskInhibit does this. If TTTaskInhibit is not zero, then application menus should not be popped up. This could simply be a matter of returning early out of the event handler if TTTaskInhibit is not zero.
Finally, after XtAppWorkProc() but before XtAppMainLoop(), a setup call to TTinitializeXt( char *filename, Widget toplevel) should be made to initialize the TaskTest. The filename is the file name for the tasks.list and toplevel is a pointer to the widget that will contain the popup prompt. This initialization call reads in the tasks, and then prompts for the first block and task with a popup window.