Davinci - GUI Module

Contents

Appendices:

Introduction

The Davinci GUI module presents the developer with a limited set of Xt and Motif widget bindings, facilitating the construction of basic GUI-driven Davinci applications.

Since the module widgets are based on Xt and Motif, most of the properties and behavior of the widgets are defined by the underlying Xt widget API. Actions are handled via callbacks, and attributes via key-value pairs (implemented using Davinci structs). All widgets are contained inside other widgets in a hierarchal fashion.

This document assumes a basic familiarity with GUI concepts, specifically the X Window System and Xt/Motif widgets. You will need a Motif reference in order to familiarize yourself with individual widgets and their attributes and callbacks. The module author used O'Reilly & Associates' X Window System series as the implementation reference.

Conventions

All Davinci code examples are in monospaced magenta.

All Davinci output is in monospaced cyan.

Getting Started

Davinci must be built with module support at compile time, and the GUI module must be loaded before the bindings are available.

Module Location

Davinci will look for modules in its default installation directory (/usr/local/lib/davinci, if not changed at build time), or if the environment variable DV_MOD_PATH is set, in that directory.

Loading the Module

To load and initialize the GUI module, enter:

load_module("gui")

Module API

The module presents the following functions to the developer:

(Redundant arguments are defined only once. Optional arguments are in italic.)

gui.create(widgetclass, parent, resources, name)
widgetclassXt/Motif WidgetClass or Davinci alias.
parentThe widgetid of the parent widget. Default is the application shell.
resourcesA struct of resource key/values.
nameArbitrary name for the widget. Most widgets ignore this value.

Create a new widget and return its handle.

gui.destroy(widgetid)
widgetidThe handle of the widget to act on.

Destroy widgetid. This will recursively destroy all of the widget's children as well. Any widgets which are realized (visible) when destroyed will be unrealized first.

Note that once a widget is destroyed, even if it's a child widget destroyed as a side-effect, its Davinci handle will no longer be valid and should not be used in any GUI module functions.

gui.get(widgetid, resourcelist, all)
resourcelistA list of resource names to retrieve. Accepts struct, text, or string.
allIf 1, return all resources, including hidden resources.

Retrieve the default list of resources, the resources specified in struct resources, or all widget resources if all = 1 is specified.

Resources are returned in a Davinci struct.

gui.set(widgetid, resources)

Set one or more resources on the specified widget.

The resource names and values must be in a Davinci struct. The struct from a gui.get() call may be modified and used in a gui.set() call. You may also use anonymous structs.

gui.realize(widgetid)

Make the specified widget visible. This recursively applies to all of the widget's children as well.

It is best to create and configure the widget and all its children before realizing the outermost widget, to avoid display problems.

gui.addcallback(widgetid, callback, eval)
callbackThe Xt callback name or Davinci alias.
evalA piece of Davinci code to evaluate when the callback occurs.

Add the named callback to widgetid. The code in eval must be a string of Davinci code, which we be evaluated each time the callback event occurs. If you use a function name, arbitrarily complex Davinci functions can be executed.

gui.removecallback(widgetid, callback)
callbackThe Xt callback name or Davinci alias.

Remove the specified callback from widgetid. The widget will no longer call any Davinci code when the callback occurs, however any internal callback handling will still occur (such as changing RadioButton states).

Available Widgets

Many of the basic Xt and Motif widgets are available. Some are implemented more completely than others. In particular, complex widget resources (those accepting non-scalar values) are less likely to be implemented. The best way to see which resources have been implemented is to create an instance of a WidgetClass you wish to use and run gui.get(widgetid, all = 1) on it.

The following widgets are currently available with varying degrees of completeness:

Davinci NameXt NameDescriptionUsabilityNotes
arrowButtonXmArrowButtonArrow-shaped push buttonComplete
cascadeButtonXmCascadeButtonMenu supportNonePending - menu implementation
comboBoxXmComboBoxCombobox SelectionHigh
commandXmCommandCommand entry dialogComplete
drawnButtonXmDrawnButtonGraphical push buttonLowPending - Pixmap support
errorDialogXmErrorDialogError message modal dialogLowPending - Modal dialog support
fileSelectionBoxXmFileSelectionBoxFile/directory selection dialogComplete
formXmFormLayout manager - free-formHigh
frameXmFrameProvides a window frame/borderComplete
labelXmLabelBasic text labelHighPending - Pixmap support
lineBoxLineBoxStretch function line drawingComplete
listXmListBasic ordered listComplete
menuBarXmMenuBarMenu supportNonePending - menu implementation
panedWindowXmPanedWindowLayout manager - vertical tilesComplete
pushButtonXmPushButtonGeneric push buttonComplete
radioBoxXmRadioBoxLayout manager for radio buttonsComplete
rowColumnXmRowColumnLayout manager - gridHigh
scaleXmScaleSliding ranged value selectorComplete
scrollBarXmScrollBarScrollbarModerateUse ScrolledList/ScrolledWindow instead
scrolledListXmScrolledListList with automatic scrollbarsModerate
scrolledWindowXmScrolledWindowLayout manager - scrollable areaModerate
selectionBoxXmSelectionBoxList with item selectionComplete
separatorXmSeparatorDividing lineComplete
textXmTextMulti-line text entry windowComplete
textFieldXmTextFieldSingle-line text entry fieldComplete
toggleButtonXmToggleButtonBoolean state buttonCompleteUse inside RadioBox
topLevelShellTopLevelShellFramed & titled windowComplete
transientShellTransientShellModal dialog shellLowPending - modal dialog support
vicarxvicBasicImageWidgetImage display & manipulationHighThird-party Motif widget [Reference]

Most widgets marked Moderate are functional and tested, but some resources are not supported. The primary unsupported resources are colors and pixmaps.

Basic Windows

ApplicationShell

All widgets must have a top-level parent. When the GUI module is loaded, it will create a hidden top-level widget. This is an Xt ApplicationShell. Only one is created, and the developer cannot manipulate it or directly reference it.

The ApplicationShell is the default widget parent, used when calling gui.create() with no parent specified.

TopLevelShell

The primary developer-controlled top-level widget is the Xt TopLevelShell widget. It presents a window frame, title bar, and a container to hold other widgets.

To create a TopLevelShell widget, enter:

top = gui.create("topLevelShell", name = "My Davinci App")

The value stored in top is a Davinci handle to the underlying Xt widget. You do not need to save the handle, however it is the only way to reference the widget later. Except in the case of simple Label widgets and the like, you'll need to save most handles.

TransientShell

Transient shells are used to implement modal dialogs and are currently not functional.

Hello, world!

The Davinci program below creates a simple window with a message in it:

load_module("gui")
top = gui.create("topLevelShell", name = "Hello!")
label = gui.create("label", top, { labelString = "Hello, world!" })
gui.realize(top)

This example may be entered interactively, placed into an executable Davinci script, or placed in a file and executed via source().

Note that the interactive Davinci command prompt is still available while the GUI window is displayed. Take care not to alter the widget handle variables while the widgets are in use.

To destroy the window (and both widgets created), either destroy it in your window manager (usually by clicking X on the title bar), or enter:

gui.destroy(top)

Either method will destroy the widgets completely. While the variables top and label will still contain values, they will no longer be recognized as widget handles and should not be used.

Resource Manipulation

All resources manipulation is performed via Davinci struct variables. The simplest resources are key/value pairs with scalar values, however arbitrarily complex objects can be used for compound resource values, such as lists of strings or multi-value feedback structures.

CSG: Create, Set, Get

While most resources can be manipulated at will, some can only be set at widget creation time, and become read-only thereafter, and some are always read-only and can never be changed by the developer. In Xt reference guides, these resources are usually labeled in a CSG column, where C, S, and G are treated as flags.

If you try to set a read-only resource, or set a create-only resource after creation time, the value will be ignored. Refer to a Motif reference manual for details.

Resource Structure

Building on the "Hello, world!" example, the labelString resource could have been set by explicitly creating a struct, as below:

labelResources = struct()
labelResources.labelString = "Hello, world!"
label = gui.create("label", top, labelResources)

Either method is acceptable, but explicitly creating the struct will make your code cleaner when dealing with a large number of resources.

Setting Multiple Resources

To set multiple resources on a widget at once, add more elements to the structure:

labelResources = struct()
labelResources.labelString = "Hello, world!"
labelResources.alignment = "ALIGNMENT_END"
label = gui.create("label", top, labelResources)

If you modify the example and add the alignment resource, you will see that the label now "sticks" to the side of the side of the window when you expand it, instead of staying centered.

Querying Widget Resources

Once a widget has been created, you can query it at any time to obtain the current resource values. The widget need not be realized. Note that resource values may change as a side-effect of "invisible" user actions, such as resizing windows.

To obtain the resources for our label widget:

resources = gui.get(label)

If Davinci is in verbose mode, you will see the structure returned:

struct, 2 elements
  alignment: "ALIGNMENT_END"
  labelString: "Hello, world!"

Resource Visibility

Calling gui.get(widgetid) will return only the default resources. These resources are the ones most commonly associated with using the particular widget. The default resource list is meant to be small and manageable, and can help developers learn what features a widget supports.

Additional resources are available on every widget, and are normally hidden to reduce spam. If you'd like to see all the widgets available, use gui.get(widgetid, all = 1). This will display all supported widget resources, including those inherited from parent widgets (more on that later).

Running gui.get(label, all = 1) from our example yields:

struct, 30 elements
  ancestorSensitive: "true"
  sensitive: "true"
  depth: 24	
  mappedWhenManaged: "true"
  numChildren: 1	
  allowShellResize: "true"
  saveUnder: "false"
  overrideRedirect: "false"
  title: "Hello!"
  wmTimeout: 5000	
  waitforwm: "true"
  transient: "false"
  baseWidth: -1	
  baseHeight: -1	
  minWidth: -1	
  minHeight: -1	
  maxWidth: -1	
  maxHeight: -1	
  widthInc: -1	
  heightInc: -1	
  minAspectX: -1	
  minAspectY: -1	
  maxAspectX: -1	
  maxAspectY: -1	
  iconX: -1	
  iconY: -1	
  clientLeader: -1	
  urgency: "false"
  iconName: "Hello!"
  iconic: "false"

As you can see, a few resources were inherited from the parent, such as title and iconName. Container and layout widgets all impart some resources on their children. Most of these will never be used by the developer, hence the idea of "default" and hidden resources.

Because some resource query operations may be costly (especially when images are involved), you can explicitly query specific resources. To query a single resource:

gui.get(label, "labelString")

Which yields:

struct, 1 elements
  labelString: "Hello, world!"

To query multiple resources, build a resource list in either a struct:

structlist = { "labelString", "alignment" }
gui.get(label, structlist)

...or a text object:

textlist = cat("labelString", "alignment", axis = y)
gui.get(label, textlist)

Both produce the same output:

struct, 2 elements
  labelString: "Hello, world!"
  alignment: "ALIGNMENT_END"
      

Pseudo-Object-Oriented Access

A natural side-effect of Davinci structure access allows you to access resources in a manner familiar to object-oriented developers:

lstring = gui.get(label).labelString

If you use this method, it's still a good idea to explicitly request the resource you need, for efficiency:

lstring = gui.get(label, "labelString").labelString

Layout Managers

RowColumn

The RowColumn widget is flexible and easy to use, providing a basic tabular layout style, very similar to HTML tables. In its simplest usage, the number of either rows or columns is fixed, and the other axis expands as widgets are added.

A simple example which uses multiple RowColumn widgets to create a push-button calculator is referenced below. The complete source code is available here:

[Example] Davinci Calc

When run, the calculator looks like this:

[Davinci Calc Application]

The example uses two RowColumn widgets: one to layout the buttons, and the other to layout the keypad and the display. It demonstrates two different packing styles. See comments in the code for details.

Callbacks

Callbacks are what make Davinci GUIs interactive. By registering a callback on widgets such as PushButton and FileSelectionBox, your Davinci application can react to user events. The callback mechanism allows Davinci code to be run when a GUI event occurs.

By default, most events are ignored unless the developer explicitly registers a callback. Some widgets have a default callback which is used when the developer has not overridden it. See the Xt/Motif references for details.

To register a callback, you need to know the internal name of the callback, as defined by Xt, and you need to define a Davinci function to respond to the callback.

Here's a simple example:

load_module("gui")

define buttonCallback() {
  printf("You pressed the button!\n");
}

top = gui.create("topLevelShell", name = "Hello!")
pb = gui.create("pushButton", top, { labelString = "Press Me!" })
gui.addcallback(pb, "activate", "buttonCallback()")
gui.realize(top)

Example Applications

Davinci Calc

Presents a simple calculator application using PushButton, TextField, and RowColumn widgets. Callbacks are also demonstrated.

[Example] Davinci Calc

VICAR DCS Tool

Presents an image manipulation tool highlighting the VICAR widget. This example uses the FileSelectionBox widget and callbacks.

NOTE: this example requires a 10-band ISIS projected cube.

[Example] VICAR DCS Tool

Known Bugs & Outstanding Issues

Reporting Bugs

Please report any bugs that you find, including instances of those mentioned below. When possible, provide code that demonstrates the bug.

Container/Layout Widgets

If you place a layout manager widget--such as a RowColumn--inside another layout manager--such as a Form, it may not be possible to control the Form resources that the RowColumn widget inherits.

You may notice similar problems if you place a complex widget--such as the VICAR widget--inside a container/layout manager. Some widgets provide their own frame and layout, and do not conform to other layout manager constraints. These widgets work best when given their own window frame.

Memory Leaks

There may be memory leaks, particularly widgets that do not clean up after themselves when destroyed as a side-effect of a window being closed by the user, or being destroyed when a parent is destroyed via gui.destroy().

Contacting the Author

If you have any questions/comments/bug reports, please send email to Eric.Engle@asu.edu.

Appendices

List Widget

The List widget uses a pseudo-resource to control its list items. The real resources items and itemCount are not directly settable by the user (although you may read them). To manipulate the item list, you must set the itemList resource. The resource is a list of strings (Davinci STRING, TEXT or nameless STRUCT).

Example:

items = cat("item1", "item2", "item3", axis=y)
list = gui.create("list", parentWidget, { itemList = items })

Valid HTML 4.01! Valid CSS!