Introduction to Using the Picoscopic User Interface

John F. Fay

October 18, 2000

(Updated by Sebastian Ude on October 02, 2001)


1. Introduction:  Graphical User Interfaces and Windowed Programs
2. Elementary PUI
2.1 Creating User Interface Widgets
2.2 Invoking PUI
2.3 Widget Callbacks
3. Conclusion

1.  Introduction:  Graphical User Interfaces and Windowed Programs

The Picoscopic User Interface (PUI) is designed to be a simple, easy-to-use graphical user interface.  It is based on the OpenGL graphics language and requires a windowing system, either the OpenGL Utility Toolkit (GLUT) or another similar one.  The gentle reader is assumed either to be familiar with OpenGL and his windowing system or at least to have their manuals at hand.

Basic to a GUI is the idea of a widget.  A widget is something that appears on the computer screen and which transfers data from the user to the program or from the program to the user.  The user can activate a widget by pressing a key, clicking a mouse button, or by using some other input device.  This lets the user send information to the program.  A widget can also display text or a graphic, allowing the program to transfer information to the user.

A windowing system requires a specific style of program which differs from a text-based program in several fundamental ways.  First, instead of starting at the beginning of a problem and stopping at the end, a windowed program starts by creating the windows and then enters an infinite loop in which it responds to user inputs.  Second, instead of having a self-contained hierarchy of functions which call each other, a windowed program is controlled by the windowing system which calls functions as it sees fit.  The following diagram shows the structure of a typical windowed program.

        |   Main Program   |  The application developer
        | Set up window(s) |  supplies this.
        |   and widget(s)  |
         | Window Manager |   The window manager library
         |  (like GLUT)   |   supplies this.
          +------+--------+---------------+---------------+----> etc.
          |               |               |               |
          V               V               V               V
     +----------+    +----------+    +----------+    +----------+
     |  Window  |    |  Window  |    |  Window  |    |  Window  |  The application
     |  Mouse   |    | Keyboard |    |   Idle   |    | Display  |  developer
     | Callback |    | Callback |    | Callback |    | Callback |  supplies these.
     +----------+    +----------+    +----------+    +----------+
          |               |               |               |
          V               V               V               V
     +----------+    +----------+     May call       +----------+
     |   PUI    |    |   PUI    |   PUI functions    |   PUI    |  PUI
     |  Mouse   |    | Keyboard |    for default     | Display  |  supplies
     | Function |    | Function |     behaviors      | Function |  these.
     +----------+    +----------+                    +----------+
          |               |
          +-------+-------+---------------+------> etc.
          |               |               |
          V               V               V
     +----------+    +----------+    +----------+
     | Widget 1 |    | Widget 2 |    | Widget 3 |  The application developer
     | Callback |    | Callback |    | Callback |  supplies these.
     +----------+    +----------+    +----------+

The functions that the application developer supplies are called "callbacks" because the windowing system or user interface system "calls back" into the application.

A windowed program with a user interface, then, consists of a main program and a set of callbacks.  The callbacks may invoke other function in turn, but these are all arranged in hierarchical trees underneath the callbacks.

A windowed program executes differently from a typical hierarchical program as well.  It begins with the main program setting up the window(s) and the user interface.  The main program then calls the window manager, which contains an infinite loop and never returns control to the main program.  (GLUT in particular has this behavior; I cannot speak for all window managers.)  When the window manager needs to redraw the display that is shown on the screen, it calls the application's Window Display Callback.  When the user moves the mouse or clicks a mouse button, the window manager calls the application's Window Mouse Callback.  When the user presses a key on the keyboard, the window manager calls the application's Window Keyboard Callback.  Other input devices cause the window manager to call other callbacks in the application.  Finally, if the user hasn't done anything for a while, the window manager will call the application's Window Idle Callback.

In building a window callback, the application developer must be sure to call the appropriate PUI functions.  The applications's Window Mouse Callback, for example, must call the PUI mouse function "puMouse" if the user interface is to process a mouse click.  The application's Window Display Callback, besides rendering any graphics in the window, must also call the PUI display function "puDisplay" in order to display the widgets in the user interface.

When the application developer's window callback calls the appropriate PUI function, PUI determines which (if any) widget the user has just activated with his mouse, keyboard, or other input device.  PUI then calls that widget's callback function which the application developer must supply and which provides the actual heart of the interface.  The callback for an input widget will probably read the text that the user has typed there and pass it on to the application.  The callback for a button widget may turn something in the program on or off.  What a callback does is limited only by the application developer's imagination.

2.  Elementary PUI

Using the Picoscopic User Interface on an elementary level requires three things:  setting up the user interface widgets, invoking PUI from the window callbacks, and supplying the widget callbacks.

PUI was written in C++, designed to be invoked from C++ programs.  The following text and examples will assume a program written in C++.

2.1 Creating User Interface Widgets

Most (or all) widgets have certain properties in common.  These are: PUI supports other widget properties, but these are enough to allow the application developer to create a simple user interface.

Each widget in the Picoscopic User Interface is an object in C++.  The application developer creates a widget by defining a variable to point to the object and creating a new object:

  puButton *button = new puButton ( 10, 10, 100, 30 ) ;

To set the properties of a widget, the application developer calls methods in the object:

  button->setPosition ( x, y ) ;                  // Sets the position of the widget to
                                                         (x, y)
  button->setSize ( w, h ) ;                      // Sets the size of the widget to (w, h)
  button->setCallback ( widget_cb ) ;             // Sets the callback to
                                                         "void widget_cb ( puObject *ob )"
  button->setLegend ( "Press Me" ) ;              // Sets the legend
  button->setLabel ( "A Button:" ) ;              // Sets the label
  button->setLabelPlace ( PUPLACE_LOWER_LEFT ) ;  // Makes the label print on the button's

Other allowed values for the label position are:

For a detailed description of these have a look at the PUI Programmers Guide.

All of these calls are optional.  If the application developer does not want to label a widget, he simply does not call "setLabel" or "setLabelPlace".  Similarly, if he has set the widget's position and size while creating the widget, there is no need to call "setPosition" or "setSize".

The following subsections describe some of the widget types that PUI supports.

2.1.1 Button

The button ("puButton") is a rectangle, usually containing a legend, which is activated when the user clicks the mouse on it.  It has two possible values, off and on; its actual value changes each time the user clicks on it.

The button has two possible methods of construction:

2.1.2 Button Box

The button box ("puButtonBox") is a large rectangle containing a set of buttons, each with text next to it, which the user can select.  It is sometimes called a radio button.  It is created by creating a null-terminated list of labels and then by calling the constructor function:

  char **labels = { "Label1", "Label2", "This is Label3", "Four", NULL } ;
  puButtonBox my_box = new puButtonBox ( x1, x2, y1, y2, labels, <0|1> ) ;

This creates a button box with lower left-hand coordinates of (x1, y1) and upper right-hand coordinates (x2, y2).  The number of buttons is specified by the number of entries in the "labels" array; each string before the NULL gets a button.  The final argument in the constructor specifies whether the user can select multiple buttons (0) or whether selecting a new button will deactivate the previously-selected button (1).

The button box should not be given a legend.

2.1.3 Frame

The frame ("puFrame") is a rectangular area that provides a background for other widgets.  It should be defined before any other widgets that go inside it or else it will cover them up.  It is constructed by the following function call: The frame should not be given a legend.  It does not support a callback either.

2.1.4 Input Box

The input box ("puInput") is a rectangular area into which the user can type text.  The user must first click the mouse inside the area and then type the text.  He concludes by pressing <Enter> or <Tab> or by clicking the mouse outside the input box.

An input box is constructed by the following function call:

The input box should not be given a legend.

2.1.5 Menu Bar

The menu bar ("puMenuBar") is easily the most complicated widget written up here.  It consists of a horizontal bar containing buttons which the user can click on.  The bar is always situated on the left-hand edge of the window and is usually in the upper left-hand corner.  It is constructed by the following function call:

Once the menu bar has been constructed, it must be loaded with its entries and submenus.  A submenu is the list that drops down when the user clicks on an entry in the menu.  A typical menu bar will have the entries "File", "Edit", "Search", and "Help".  Under the "File" entry is a submenu with entries "New", "Open", "Save", "Save As", a separator, "Page Setup", "Print", another separator, and "Exit".  Each entry in the submenu has its own widget callback function.  To create a submenu, the application developer must first create two null-terminated lists of character strings and callback functions:

  char      *file_submenu   [] = { "Exit", "-----",  "Print",  "Page Setup", "-----",
                            "Save As",  "Save",  "Open",  "New", NULL } ;
  puCallback file_submenu_cb[] = { exit_cb,    NULL, print_cb, page_setup_cb,    NULL,
                           save_as_cb, save_cb, open_cb, new_cb, NULL } ;

Notice here that the submenu entries are placed in the list from bottom to top.  After the last character string is a NULL entry; this tells PUI that there aren't any more entries in the submenu.  The callbacks for the separators are also null, indicating that there is no callback for the separator.  The submenu is added to the menu bar with a call to the "add_submenu" function:

  menu->add_submenu ( "File", file_submenu, file_submenu_cb ) ;

Further calls to "add_submenu" add the other submenus:

  char      *edit_submenu   [] = {  "Select All", "-----",  "Paste",  "Copy",  "Cut",
                            "-----",  "Undo", NULL } ;
  puCallback edit_submenu_cb[] = { select_all_cb,    NULL, paste_cb, copy_cb, cut_cb,
                               NULL, undo_cb, NULL } ;
  menu->add_submenu ( "Edit", edit_submenu, edit_submenu_cb ) ;

  char      *search_submenu   [] = {  "Find Next",  "Find", NULL } ;
  puCallback search_submenu_cb[] = { find_next_cb, find_cb, NULL } ;
  menu->add_submenu ( "Search", search_submenu, search_submenu_cb ) ;

  char      *help_submenu   [] = {  "About",  "Help", NULL } ;
  puCallback help_submenu_cb[] = { about_cb, help_cb, NULL } ;
  menu->add_submenu ( "Help", help_submenu, help_submenu_cb ) ;

When the last submenu has been added, the application developer must close the menu bar:

  menu->close() ;

This is absolutely necessary or else PUI will think that any widgets the application defines later are supposed to go into the menu bar.

The menu bar does not support a legend.  It does not support a callback itself, although all the entries in the submenus certainly do support callbacks.  Repositioning and resizing a menu bar will be problematic as well.  There may be an occasion when a menu bar can take a label, but this would be rare.

2.1.6 One-Shot Button

The one-shot button ("puOneShot") is a button whose value is always reset immediately to zero after it has been activated.  In all other respects it is the same as the regular button.  It is created in the same way as the button:

2.1.7 Text Box

The text box ("puText") is an output-only widget which displays a character string.  It is created by the following constructor: The application actually adds the text by assigning a label and a label place to the text box.  The label place will determine where relative to the position of the text box the text itself will appear.

2.2 Invoking PUI

The summary of invoking PUI is very simple: The application developer must create window callbacks for the window manager to call when the user interacts with the computer.  The following are taken from GLUT, but another window manager will have similar callback requirements.

2.2.1 Main Program Code

In the main program, the application developer usually places the following or similar statements:

  // Create the GLUT window and the OpenGL context

  glutCreateWindow       ( "PUI Application"  ) ;

  // Set up the GLUT window callbacks

  glutDisplayFunc        ( displayfn  ) ;  // display the window
  glutMouseFunc          ( mousefn    ) ;  // process mouse clicks
  glutMotionFunc         ( motionfn   ) ;  // process mouse moves
  glutPassiveMotionFunc  ( motionfn   ) ;
  glutKeyboardFunc       ( keyboardfn ) ;  // process key presses
  glutIdleFunc           ( displayfn  ) ;  // what to do when nothing's happening

  // Initialize PUI

  puInit () ;

  // Set up the widgets here

2.2.2 Window Callbacks

The application's window callbacks should generally look like this.  Again, these are written assuming GLUT; other window managers should be similar.

  void displayfn ( void )
    // stuff to display any non-PUI drawings

    // update any PUI widgets that update by themselves ... like a timer display

    // redisplay PUI

    puDisplay () ;

    // Finish up

    glutSwapBuffers () ;
    glutPostRedisplay () ;

  void mousefn ( int button, int updown, int x, int y )
    // Invoke the PUI mouse function

    puMouse ( button, updown, x, y ) ;
    glutPostRedisplay () ;

  void motionfn ( int x, int y )
    // Invoke the PUI mouse motion function

    puMouse ( x, y ) ;
    glutPostRedisplay () ;

  void keyboardfn ( unsigned char key, int, int )
    // Invoke the PUI keyboard function

    puKeyboard ( key, PU_DOWN ) ;
    glutPostRedisplay () ;

2.2.3 Miscellaneous Other Tidbits

If the application developer wishes to delete a widget, he should not delete the object directly.  Instead he should call the PUI function "puDeleteObject ( ob )" with his widget pointer as the argument.

2.3 Widget Callbacks

The final pieces to the puzzle are the widget callbacks.  These are of the form

  void widget_cb ( puObject *ob )
    // code goes here

where "puObject" is the C++ type for a generic widget.  When PUI calls a widget callback, it passes to it the address of the widget whose activation caused the callback.  This is useful because it allows a single callback for multiple widgets:

  void widget_cb ( puObject *ob )
    if ( ob == button )  // the button from section 2.1
    else if ( ob == my_box )  // the button box from section 2.1.2
      printf ( "Error - unknown widget" ) ;

A widget callback needs to be either a regular function (global or defined in the same file that contains the widget's definition) or a static member function of a class.  It cannot be a regular member function of a class.

2.3.1 Widget Values

It is frequently very useful in a callback to know the value of a widget.  A PUI widget has an integer value, a floating-point value, and a character string value.  These values are kept synchronized as much as possible.  For example, if the application assigns an integer value to a widget, the floating-point value is set equal to the integer and the integer is printed into the character string.

An application program can use the following function to retrieve the value of a PUI widget ("ob" is the pointer to the object):

  int ivalue1 = ob->getValue () ;  // No argument, return the integer value

  int ivalue2 ;
  ob->getValue ( &ivalue2 ) ;  // Place the integer value in the argument

  float fvalue ;
  ob->getValue ( &fvalue ) ;  // Place the floating-point value in the argument

  char svalue[PUSTRING_MAX] ;  // Create a string
  ob->getValue ( svalue ) ;    // Copy the string value into the argument
                               // Note that the argument is of type "char *"

  char *sptr ;
  ob->getValue ( &sptr ) ;  // Have the pointer point to the string value
                            // Note that the argument is of type "char **"

The values of the various widgets have the following meanings:

3.  Conclusion

This has been offered as an introduction to the Picoscopic User Interface.  It makes no warranty as to its accuracy although efforts have been made to make it correct.  It is offered in the hope that the gentle reader will find it useful.

The PUI has considerably greater capabilities than have been listed here.  There are well over a dozen additional types of widgets.  Additionally, the application developer can do many more things with this widgets than have been described here.

"As to more than these, my son, beware.  Of the making of many books there is no end, and in much study there is weariness for the flesh." - Ecclesiastes 12:12