Some notes on the GUIKit

Introduction

The GUIKit is a recent addition to Mathematica - supplied as part of the software at version 5.1 - which uses J/Link to add a Graphical User Interface (GUI) to Mathematica applications. For what follows it is important to use Version 1.0.2 (or better) of GUIKit. To determine which version you have, load the GUIKit and execute:

GUIKit`Information`$Version

If necessary, the latest version can be downloaded from the GUIKit online site.

Although the documentation for the latest version is reasonably complete, and it is possible to use it to create some impressive interfaces, I do not find it as easy to use as I would like. These notes and program fragments are designed to augment the documentation - not replace it. If you wish to use any of the (free) functions defined here, I suggest you click above to download the notebook version of this page. Feedback and suggestions regarding this page will be extremely welcome. If you use the Super Widget Package, please ensure that the copyright notice remains intact if you pass it on.

Please note that the implied criticism of the GUIKit design may reflect my lack of understanding of this rather new Mathematica technology, and that GUIKit is, in any case, an interesting and usable extension to Mathematica.

Relationship to J/Link

The GUIKit is built upon J/Link - the package that links Mathematica to Java. Although J/Link is extremely powerful, GUIKit inherits two serious problems from J/Link:
•   Java runs as a separate process. This means that if you position a Java (i.e. J/Link) window over a notebook, and click on the notebook, you will conceal the window – even though the user is expected to respond to the window. This can give the appearance of a hung program.
•   Mathematica graphics can be rendered in a Java window, but if a mouse-listener is attached, the {x,y} coordinates returned are unrelated to the graphical coordinate system. See the Interactive Graphics Package for a solution to this problem for J/Link applications. Here you will see how to solve this problem for the GUIKit – which can result in some great applications!

GUIKit and Mathematica Contexts

Using the GUIKit, a window definition typically consists of a nested sequence of Widget calls, in which each Widget defines some element of the window – such as a portion of text, an image, button, slider, etc. – or, indeed, the window (the "Frame" widget) that is used to wrap up all these elements. Each Widget comes with many options, and can include sections of Mathematica code inside Script blocks. This code is meant to execute in specific situations – such as when a mouse button is pressed – and provides the feedback from the GUIKit window to the rest of your Mathematica code.

Unfortunately, a Widget definition can be stored and used more than once. This would typically be the case for a fairly low-level widget – such as (say) some kind of enhanced text box – it is not likely to be a problem for the average GUIKit user. However, the GUIKit design solved this problem in a very draconian way. All the symbols belonging to the Global` context in a script are moved into a private context, and the $ContextPath is stripped bare while the script is being executed! In addition, certain GUIKit functions, such as PropertyValue, actually spawn private versions of themselves which are installed in the private script context and are not equivalent to their public siblings! Because of all this overkill, some careful programming is required to use GUIKit in many applications – use the option IncludedScriptContexts -> {$Context} on GUIRun and GUIRunModal unless you find these context manipulations useful..

A simple GUIKit definition

Here is a function which displays a message in a box with "OK" and "Cancel" options. It returns a code to indicate which button was pressed:

Needs["GUIKit`"];

AskOK[s_String, title_String]:=Module[{ans=0},
GUIRunModal[
Widget["Frame",{
"title" -> title,
"Resizable" -> 
                            False,
WidgetSpace[5],
{WidgetFill[], 
                            WidgetSpace[20], 
                            Widget["Label", {"text" -> 
                                  s, "font" -> 
                                  Widget["Font", 
                                    InitialArguments -> {"Times New \
Roman", PropertyValue[{"class:java.awt.Font", "Plain"}], 18}]}], 
                            WidgetFill[], WidgetSpace[20]},
WidgetSpace[5],
{
WidgetFill[],Widget["Button",{"text" -> "OK",
BindEvent["action",Script[ 
ans=1;
CloseGUIObject[GUIObject[]]
]]
}],
WidgetSpace[5],
Widget["Button",{\("text" -> "Cancel"\),
BindEvent["action",Script[ 
ans=2;
CloseGUIObject[GUIObject[]]
]]
}],
WidgetFill[]
},
WidgetSpace[5]
}],
\(IncludedScriptContexts -> {$Context}\)
];
ans
]

This tests the function:

AskOK["OK to reformat your hard disk?", "Caution!"]

As you can see, this definition is rather complicated to perform such a simple task! Part of the problem is that few of the defaults in GUIKit seem to be ideal here. GUIKit's defaults seem to be the basic Java defaults, which are often not very useful. Thus, the "Label" widget seems to produce text which is a little too small, also the default layout is not spaced out at all, and you have to make special provision to cause the buttons to directly close the box and return a result.

Note the use of WidgetSpace to create vertical space above and below the text and buttons, and horizontal space between the two buttons. WidgetFill before and after the two button widgets causes them to become centered.

Several of the options (such as "Resizable") that are used in the above example, are not documented. You can get a list of methods, events, and properties using the following function:

GUIDetails[s_String] := 
      Module[{ref}, 
ref = 
          GUILoad[Widget[s, Name -> "XXX"]]; 
        Widget["Dimension", {"width" -> 50, "height" -> 
                  60}]
ans = 
          GUIInformation[ref, "XXX"]
]

This call should work for any named Widget, although you may get an error message if the widget in question requires arguments. Note that most of the names returned in this way are of a very general nature and not generally useful. You may want to extract the list and use complement it with another type of widget to isolate the really relevant information. Except where the names enable you to guess how to use the information, it is necessary to use Java documentation to understand this completely.

GUIDetails["textfield"] // Short

Window - what window?

Return now to the AskOK function and activate it. Now click on this notebook (moving it if necessary) and observe how the GUIKit window, which is running as a separate process, is concealed. The result can be intensely confusing for an end user, who will probably conclude that your program has hung! To avoid this problem, you may wish to conceal all the Mathematica notebooks while a GUIKit window is open. Notice that this function stores the list of notebooks that it has hidden and only makes those notebooks visible again afterwards. This is because various packages, such as J/Link, use hidden notebooks as part of their operation. It is obviously important that these are not made visible as a side-effect of the operation.

concealed˘notebooks = {};
ConcealNotebooks[opt_] := 
      If[opt, 
concealed˘notebooks = 
          Select[Notebooks[], Options[#, Visible][[1, 
                  2]] &]; 
Scan[
          SetOptions[#, Visible -> False] &, 
          concealed˘notebooks], 

Scan[
          SetOptions[#, Visible -> True] &, 
          concealed˘notebooks]; 
concealed˘notebooks = {};
]

GUI programmers refer to modal and modeless windows. The above window is termed a 'modal' window because the program enters a different 'mode' while the window is being displayed. Modeless windows (displayed using GUIRun) are more like the palette windows (actually tiny notebooks) that open when you start a newly installed Mathematica. It is normal for windows of this kind to get covered up – since the program does not enter a different mode for the time while they are visible.

Observe that if you are contemplating a large GUI application, it is important to think early on about the relationship between your GUI windows and the existing notebook interface. If you want to interact with your user via a modal window, will he or she need to refer to information in a notebook in order to respond?

Using textfields

Here is an example of a "TextField" Widget. Almost every serious GUI application will use several of these to prompt the user for input.

\(Widget["TextField", 
{"text" -> "42"}
] // GUIRunModal\)

If you play with this example a little, it is obvious that the  raw "textfield" widget will require quite a lot of customisation if it is going to share the 'look and feel' of a typical Windows application (say). In particular:
• The window opens with the cursor on the left. Thus if the user types a '1' character, it will appear in the 100's field. In  a normal Windows application the cursor appears on the right, or the whole field is selected, so that it can be replaced by typing over it.
• If the textfield is going to represent an integer (say), it would be nice if the user were prevented from typing anything else. 
• In a sophisticated application, it is normal for displayed data to vary, both because of user input, and as a result of programmed interactions – perhaps as a result of user input into another, related field. It is less than obvious how to achieve this effect using GUIKit.

While some of these concerns may sound  trivial, users of GUI applications expect everything to work in a predictable way, and an idiosyncratic GUI application may be very unappealing.

A more substantial GUIKit example

This notebook contains a serious GUIKit example, kindly provided by Daniel Huber of Metohm Ltd. It illustrates what can be achieved with GUIKit – and the amount of work required! This notebook required Mathematica 5.1 and GUIKit 1.0.2.

Super Widgets

In conclusion, GUIKit Widgets give you the tools to build a Java GUI, but at a remarkably low level. A great deal of additional code is needed to 'wire up' the widgets to achieve the following :

•  Provide numeric fields that respond appropriately.
• Extract data from the GUI into Mathematica variables.
• Provide appropriate feedback for interactive graphics applications.
• Enable applications to update the contents of widgets programmatically.
• Handle the greying of widgets/menu items that should not be used in particular situations.

 I have written the Super Widget Package to address these issues, and unleash the real power of the GUIKit in Mathematica.