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.
All Davinci code examples are in monospaced magenta
.
All Davinci output is in monospaced cyan
.
Davinci must be built with module support at compile time, and the GUI module must be loaded before the bindings are available.
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.
To load and initialize the GUI module, enter:
load_module("gui")
The module presents the following functions to the developer:
(Redundant arguments are defined only once. Optional arguments are in italic.)
widgetclass | Xt/Motif WidgetClass or Davinci alias. |
---|---|
parent | The widgetid of the parent widget. Default is the application shell. |
resources | A struct of resource key/values. |
name | Arbitrary name for the widget. Most widgets ignore this value. |
Create a new widget and return its handle.
widgetid | The 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.
resourcelist | A list of resource names to retrieve. Accepts struct , text , or string . |
---|---|
all | If 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
.
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.
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.
callback | The Xt callback name or Davinci alias. |
---|---|
eval | A 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.
callback | The 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).
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:
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.
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.
Transient shells are used to implement modal dialogs and are currently not functional.
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.
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.
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.
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.
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.
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!"
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"
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
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:
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 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)
Presents a simple calculator application using PushButton, TextField, and RowColumn widgets. Callbacks are also demonstrated.
[Example] Davinci Calc
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
Please report any bugs that you find, including instances of those mentioned below. When possible, provide code that demonstrates the bug.
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.
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()
.
If you have any questions/comments/bug reports, please send email to Eric.Engle@asu.edu.
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 })