Object Prompting with the MOE Interface Toolkit
Chemical Computing Group Inc.
Graphical User Interface (GUI) programming for medium to large applications has traditionally been a complicated undertaking, particularly when designing modeless, multi-window applications.
The SVL programming environment in MOE is multi-tasking. This aspect offers a significant advantage to GUI programming with the SVL Graphical Interface Toolkit for reasons which are not readily apparent. This article illustrates some of the key issues, shows how they have been resolved in SVL and MOE, and presents an example of modeless GUI programming.
The advantages of multi tasking over single tasking is easily illustrated with an example. Operating systems such as DOS (on Intel based machines) are single tasking; that is, only one application / task may run at one time. Unix and Microsoft Windows, on the other hand, are multi tasking; i.e., any number of applications or tasks may run at one time. The advantage of the latter group of operating systems is apparent to anyone who has ever needed to switch between two running programs, or run more that one program at a time. The ability to switch between tasks has become standard behavior, and is expected by most users.
A modal application is one where the flow of the program controls the actions of the user. Conversely, in a modeless application, the user is in control of the program. Consider a situation where an application provides a series of prompts for input data. A modal application requires the user to enter all input data for all prompts before doing anything else within the application. This might be acceptable for single parameter input (e.g., a filename), but for context-sensitive data input, or simple input following a moderately long calculation, modality is unacceptable. For example, the word processor I'm using to write this article is modal and doesn't let me check my spelling and change my fonts at the same time. I must close the spell checker window before opening the font window, thereby losing the position of the spell checker within my document.
A modeless application allows the user to temporarily interrupt an operation and perform another task: the user is not confined to a single course of action. The multi-tasking operating systems mentioned above provide modeless task switching; i.e., the user is allowed to switch between applications at any time. Some operating systems achieve this better than others. The result is a modeless environment between applications. Modality within an application is analogous and hence, the trend in software today is toward modelessness. Not surprisingly, modeless applications are more difficult to write than modal applications. For the most part this difficulty is caused by the GUI toolkits in use.
The problem lies in the nature of event-based programming. Whereas a program to perform a calculation spends all of its time executing code, user interface code spends most of its time waiting for the user to do something (i.e. waiting for an event), usually a keystroke or mouse click. A modeless application must handle all possible user events in all states of the application. Normally, each event triggers one or more "callbacks" in the application. A callback is a function defined by the application, registered with the operating system, and called when a particular event occurs. A push button in an application window, for example, will and register a callback function to be called when the user clicks that button. Flow control is then returned to the program in that callback function so that it may perform some action. A program may register thousands of such callback functions. Since virtually any event can happen when the application is in virtually any state, the application effectively has thousands of entry points scattered throughout multiple unrelated source files. Lastly, while modeless, event-based applications are difficult to create, they are also difficult to debug because the sequence of events leading to an offending operation is not easily predictable. All in all, modeless event-based interface coding is not for the squeamish.
The SVL interface toolkit is designed to allow SVL programmers to effortlessly create modeless GUIs in a multi-tasking environment. MOE runs multiple SVL tasks analogously to an operating system running multiple applications: each application need not be aware of the other, and likewise each SVL task need not be aware of any others. In MOE, each prompting task (i.e. a task which is waiting for user input) may assume that it is the only one prompting, or effectively modal. This eliminates the traditionally complicated modeless event-based coding described above. The user, on the other-hand, interacts with multiple modeless windows. SVL window code is compact, simple, centralized, and easy to debug.
We will illustrate the multi-tasking nature of the MOE GUI Toolkit by exploring the ways in which an SVL program can interact with the user. The traditional method of getting user input in a is via a standalone window, or shell (usually modal). The user typically enters requested information and pushes the OK button. This method works quite well for simple data input, but loses its functionality when a more intimate interaction between user and application window is desired. A good example of this intimate interaction would be an SVL program which interactively deletes atoms as the user clicks on a molecule, as shown below:
This type of prompting is elegant, intuitive, and essential in many situations. A standalone shell window is inappropriate for the task. It is far more intuitive for the prompter to reside right in the window the user is working with. The user also benefits from the immediacy and centralization of actions and observed results.
In most applications, such prompting is modal. In MOE, however, all of the menu items are still active and all other windows are still active. In this way, the user can proceed to delete an atom, or perhaps issue some rendering commands to expose the atom to be deleted before actually deleting it.
Let's look at exactly how one would write a program to interact with the user in such a manner. To achieve the behavior, the application window must provide both a physical place for the prompter to reside, and a communication protocol to exchange information about user actions. The prompting SVL program must interact with user actions (mouse clicks) within the application window. In the case of prompting for atoms to delete, the prompter must be able to know which atom the user clicked on.
Such interaction normally requires a high level of intimacy between application window and prompter. The SVL Interface Toolkit in MOE renders trivial the task of creating an embedded prompter as shown above.
Writing an embedded prompter window in SVL is nearly identical to writing a standalone shell window. The same SVL functions are used to create and operate both types of window. The only difference is in specifying the application window in which to embed itself, and the type of object to prompt for. The SVL code to create the prompt shown above is as follows:
It is the location attribute of the window that indicates that the prompt shell should be placed in the MOE main window prompt area. The mode attribute specifies that atoms are being selected.
Several of MOE's application windows support (i.e. provide placeholders for) prompters. Each such window has one or more prompting modes. These modes which types of objects may be selected by the user. In the Sequence Editor, for example, one could have different prompters for deleting residues and chains. The following application window locations and prompting modes for each are listed below:
Locations for the first three application windows listed are simple tokens because only one of each window exists in the system. Since multiple database viewers may exist at once, the key of the specified viewer must also be included in order to prompt in the correct window. The return value mentioned above is returned as the value of the prompter interface object, exactly as with any other interface object.
Just as with standalone shells, prompters may house child objects to change parameters while prompting. The following image shows a prompter which mutates atoms into the element specified in the radio buttons. This particular prompter loops indefinitely (until canceled with the Esc key), allowing the user to sequentially change multiple atoms.
When a prompter is activated, the user will see the command line of the application window disappear, and the prompter take its place. The mouse cursor will then change to a cross-hair while over the prompt region in the application window. Left button mouse clicks on the prompt region are intercepted and sent to the prompter, instead of performing their usual function. Invalid mouse clicks are ignored. All other functionality in the window (and the rest of the application) is still available to the user. The prompter may be cancelled at any time by hitting <Escape>. If a prompter is activated while another is already active, the prompts will stack (LIFO), and each may be serviced or canceled as desired. Mouse clicks (selections) are only sent to the visible (most recent) prompter. Thus, the SVL programmer need not be concerned with other prompters, and simply assume they are the only prompter active.
A good combination of standalone and embedded prompting can turn an awkward interface into an efficient one. The SVL Interface Toolkit, embedded within SVL and MOE, makes it an easily achievable goal.