[R-gui] Re: XML specifs for R GUIs dialog boxes

thomas.friedrichsmeier@ruhr-uni-bochum.de thomas.friedrichsmeier@ruhr-uni-bochum.de
Tue, 26 Nov 2002 19:08:07 +0100


> - Since someone must initiate a document for the specifs, and Thomas
> seems to have something almost ready, I proposed him to make a draft.
> At this stage, modifications in a document will be more constructive
> than loosy discussions in R-SIG-GUI.

Ok, here's a rough draft. Note, that it is written from my subjective 
point of view, of what is needed and how it should be done. I'm 
picking up a few points raised in the discussion so far, but I'm 
simply ignoring others. This draft should therefore only serve as an 
aid in discussion.

I call it "specification of a plugin-description language for R-
frontends". "Plugin" refers to the fact, that the modules should be 
easy to insert into any R-frontend, preferrably but not necessarily 
without recompilation, but in any case without changes to the 
application-code. "R-frontends", because I'm currently only 
considering R. Extension to multiple backends (MatLab etc.) will 
require providing multiple variants of the "code"- and "result"-
sections (see below).

First of all, a few general considerations:
- The specification should be useful both of the general approaches 
"extend R to have a GUI" and "build a GUI around R". It should make 
as little assumptions as possible on what data is stored where, how 
exactly it is passed to and from the backend etc.
- It should be useful even to applications implementing only a 
limited subset of functionality, i.e. make as little assumptions as 
possible on what the application can interpret. Where advanced 
specifications are allowed, these should be designed to be truely 
optional, i.e. the plugin should still be useful, if those 
specifications are not handled by an application (though they may be 
used to enhance the functionality for applications that can handle 
them).
- It should focus on describing core-functionality like "for a 
correlation you need at least two numeric vectors, and you can use 
these options". It should try to leave most fancy additional stuff to 
the application. It will however not be limited to describing core-
functionality.
- In general, the particular application using the plugin should be 
free to provide it's own consistent look and feel. That is, the major 
goal is not to provide a similar look-and-feel of a certain plugin 
across different applications using it, but rather consistency across 
different plugins used in the same application. Therefore, the 
specification will not be tailored towards allowing easy control of 
the exact look of a dialog.
- In particular the specification should not make any specific 
assumptions on how e.g. context-help is invoked on a widget (right-
click, short-cut, help-button, mouse-over...) or on how values can be 
entered (pasting, drag-and-drop, selection only vs. allowing manual 
editing, handwritten input via a stylus...).
- Recurring tasks (such as selecting a vector/variable) should be 
handled by standard "widgets" whereever possible, custom solutions 
should only be used, when there is no good way to solve a problem 
using only the standard widgets. See above.
- The specification will not describe the entire GUI of a plugin. The 
application will take care of providing standard-elements such as a 
"help"-button, "close"/"submit" and anything it thinks might be a 
good idea. The specification may however give a hint, if some of 
these standard-elements are not needed (not described below, yet).

General structure:
- The specification is written in an XML-variant.
- It is divided into five or six main sections, which will be 
described in more detail below:
a) a section "location", which describes, where the plugin logically 
belongs (e.g. in a menu-structure), and what it should be called.
b) a section "layout" describing wich widgets are used, and how they 
should (preferrably) be placed, labeled etc.
c) a section "code" describing how the values entered into the 
widgets should be used, i.e. what the resulting R-command should be
d) a section "help" providing a use-centered help on the plugin
e) a section "output" describing, how the output should be handled, 
e.g. what sort of information will be provided in R-output and how it 
can be parsed.
f) potentially a section "dynamics" providing the ability to 
dynamically change things in the GUI (something like events).

The sections is detail:
"location":
Only two different tags are allowed in this section: further 
"location"-tags and "entry"-tags. "location" describes a (new) level 
in the hierarchy (e.g. a sub-menu), "entry" marks the end-point, 
where the plugin will finally be placed (e.g. a menu-entry). The 
structure is strictly hierarchical, but it is allowed to specify 
multiple "locations" for a plugin:

	<location>
		<location id="first_menu" label="Analysis">
			<location id="x_tests" label="Fictional Tests">
				<entry id="my_test" label="Some purely fictional test"/>
			</location>
			<location id="y_tests" label="Absurd Tests">
				<entry id="my_test" label="Some purely fictional test"/>
			</location>
		</location>
		<location id="extras" label="Extras">
			<entry id="my_test" label="Some purely fictional test"/>
		</location>
	</location>

The location-tag takes the following attributes:
id: An internal identifier, that will be used to identify a point at 
a certain level in the hierarchy. E.g. if we have two plugins, both 
specifying "first_menu" as the id of the top-level location, this 
means, they will both be placed under that branch of the hierachy. If 
a certain id does not yet exist at a certain level in the hierarchy, 
the application will create that and label it:
label: The label for this branch of the hierarchy. Will only be used 
for the first plugin encountered that creates this branch.
The entry-tag takes the same attributes:
id: It two entries at the same level in the hierarchy exist, the one 
encountered later will overwrite the first (this will make it 
possible for the user to load an enhanced or own-created plugin, that 
will be used instead of the standard-plugin for the same function).
label: The label to be used.

In the above example, the plugin is placed in three different places:
Analysis->Fictional Tests->Some purely fictional test
Analysis->Absurd Tests->Some purely fictional test
Extras->Some purely fictional test

Generally it is however desirable to use only a single unique place 
per plugin.


"layout": It probably not the right term, since it also describes a 
lot of the functionality. In general this section is about creating 
and placing widgets. It allows two types of tags:
a) tags used for arranging the widgets: "row", "column", "tabwidget" 
and "tab"
b) tags describing widgets (both "active" and "passive"/decorational 
ones). To be detailed below.

ad a):
- The "row"-tag signals, that all widgets or layouts enclosed in it 
should be placed next to each other horizontally (from left to right)
- Similarily widgets/layouts in a "column" will be arranged 
vertically.
- Both tag take the attributes "width" and "height". These take 
integer-values that are interpreted as percent of the space available 
in the parent layout (or dialog), e.g.
	<row width="100">
		<column width="30">
			...
		</column>
		<column width="70">
			...
		</column>
	<row>
"width" and "height" are only hints to the layouting engine, and may 
not be taken literally.
[alternative approach: use something more similar to <table>, <tr>, 
<td> in HTML, allowing "colspan" and "rowspan" as attributes]

- The <tabwidget>-tag takes as child elements <tab>-tags. Generally 
only one <tab> will be visible to the user at a time, and the 
<tabwidget> provides a way to switch between the tabs (this will not 
necessarily have to be real "tabs", the <tabwidget> might also 
provide buttons for the different tabs, with the tabs opening up in 
additional windows). <tab>/<tabwidget> should only be used, when 
there are a _lot_ of options not commonly used, that can not sensibly 
be shown all at the same time. The <tabwidget> does not need to 
occupy the whole dialog, i.e. does not have to be at the top-level. 
If it is not at the top-level, it takes the "width" and "height"-
hints just like any other widget (see below).

ad b):
The following widgets are allowed. All widgets allow "width" and 
"height"-hints as described above:

"passive" widgets:

<text [wordwarp="boolean"]>Text to be displayed</text>:
A simple text/label. "wordwrap" specifies, whether the text is 
wrapped automatically as required. A subtag <br/> is allowed for 
manual breaks.

<frame><potentially furhter widgets/layouts></frame>
If there are widgets/layouts enclosed in the frame-tag, the frame 
will be shown as a rectangle with those widgets insides it. If not, 
it will be shown as a line (vertical in rows, horizontal in columns).

"active" widgets:
Mostly all active widgets _require_ a unique id="string"-attribute 
used to identify them. All allow a label="string"-attribute (some 
require one), that describes a simple label directly attached to the 
widget. Further, all widgets allow a sub-tag 
<contexthelp>Description</contexthelp> that describes context-help 
that can be shown for this widget (in whatever way).

- option-widgets:

<radio [columnheight="integer"]></radio>:
A group of radio-buttons. All <option> tags enclosed are radio-
exclusive. Columnheight describes, how many <option>s should be 
placed below each other, before starting a new row of buttons. The 
<radio> automatically comes with a "frame" or something similar 
indicating that the options belong together, so there is no need, to 
place the <radio> inside a <frame>.
<option value="string" label="string" [checked="bool"]/>:
Represents a single option in a <radio>. Use checked="true" to 
specify that this button should be selected at start-up. Of course, 
only one may be set to "checked". "label" is the text shown next to 
the option. "value" is what becomes the value of the radio, if this 
option is checked. It is a string value, but of course that string 
may also be the string representation of a number. The <option>-tag 
does not need an "id", since it belongs to the <radio>, that already 
has an id. Of course it may have a <contexthelp> however.

<checkbutton label="string" value="string" [checked="bool"]/>:
Mostly like a radio-option, except that it is not contained in a 
group, since all buttons may be checked individually. That also 
means, that the button requires the id-attribute!

<select selectable="integer"></select>:
A string selector. "selectable" specifies, how many options may be 
selected simultaniously (0 for all). Takes as sub-tags the <option>s 
as described for the <radio>.

Alternative (and probably better) approach to the above three 
widgets:
<optionselector selectable="integer" 
[typehint="buttons|list"]/><option>s</optionselector>
This would have the advantages, that a) the application would not 
have to be able to provide e.g. buttons, since a "list" can serve the 
same functionality. Also, the application could decide on it's own, 
what type of widget might be appropriate, e.g. buttons for less that 
6 options (radiobuttons, if selectable="1"), list for more. b) It 
would be more abstract, and that'd make it easier to e.g. use options 
generated from an R-object.

- object-selection-widgets:

<varselector [offertype="string+string...;string..."]/>
A widget, that presents a complete (preferrably hierarchical) list of 
all R-objects available. If "offertype" is specified, only objects of 
the given type(s) (e.g. numeric+vector) are offered (of course 
container-objects like lists and frames will always be shown). The 
"+" signals, that both conditions are required for an object to be 
acceptable. The ";" represents an OR.

<varslot source="string" type="string+string...;string..." 
min="integer" max="integer"/>
Holds at least "min", at most "max" variables (-1 for no limit). The 
widget will likely look different, depending on whether it can only 
hold a single or multiple objects. Since there may be more than one 
<varselector> in a dialog, "source" holds the "id" of the varselector 
that can be used as a source (of course you can still enter values 
manually). "type" specifies a list of acceptable object-types (see 
above).
Note: The varslot is the only widget, that might potentially hold 
"invalid" entries. The application will have to check, whether the 
varslot is valid!

<interaction sources="string;string;string"/>:
Used for selecting interactions. "sources" is a ";" separated list of 
<varslot>s that hold the objects, between which interactions may 
occur.

<input type="string" 
[typehint="slider|lineedit|textfield|dial|spinbox..."]/>:
Takes a discrete value as input (either numeric or string ("type")). 
"typehint" suggest, what sort of widget should be used - the 
application may decide to ignore that hint if e.g. it does not 
support that widgettype.

- output-widgets:

<graph/>:
For interactive graphs. The output section will have to describe, 
when and how to display something in this widget. Keep in mind, that 
some applications might chose to display <graph>s outside the dialog 
itself in a separate window, so make sure, the layout remains 
acceptable in this case. Ambitious applications will likely provide 
means to zoom/rotate/scale the graph, but this will not be detailed 
in the plugin-specification.


"code"-section:
approach a):
	<code>
		t.test ($x$, $y$, "$hypothesis$")
	</code>
Everything between $-signs is interpreted as the id of a widget. It 
will be replaced with the value of that widget.
approach b):
	<code>
		t.test (<?php echo "$x, $y, \"$hypothesis\""; ?>)
	</code>
Of course in the example nothing is won, but using some scripting-
language like PHP may come in handy for complex code.

Either way, besides providing values for $x$, $y$..., the application 
should also be able to fill in the following:
$x.valid$: boolean, whether the widget hold a valid entry
$x.filled$: boolean, whether the widget holds something
$x.type$: string, what type of data the widget holds
$x.name$: string, the name of the object with escaped quotes, so it 
can be used inside e.g. print ("$x.name$")
$x.label$: string, some applications may want to provide a more 
lengthy description for an object. Applications that don't, will 
place an empty string.
$x.count$: integer, if more than one object/option may be selected, 
returns how many are selected.


"help"-section:
No real ideas on this, yet. plain HTML-body? Include something 
generated from the context-help?


"output"-section:
A very vague idea:
	<output>
		<returns>
			<value name="string" [label="string"] type="string" 
description="string"/>
			...
		</returns>
		<parse>
			<?php /* parse output into separate variables if necessary. Those 
variables are described
					above in the <returns>-section. */ ?>
		</parse>
		<show>
			<?php /* generate an HTML-version of what the output might look 
like. This only generates a
					suggestion. The app may decide to do it's own formatting. */ ?>
		</show>
	</output>


"dynamics"-section:
An even more vague idea:
	<dynamics>
		<on event="x.changed" do="y.update"/>
		<on event="x.changed" do="z.changeTo(x.valid)"/>
	</dynamics>
Here we're really getting into complicated stuff. I propose to use 
only one type of event:

.changed : When the value of a widget was changed

And a limited number of functions:

.update : make the widget check, whether it is still valid, or re-
read it's 
contents (esp. useful for the <graph>-widget
.changeTo (newValue) : (newValue passed as string, but may represent 
a number)
.setEnabled (boolean) : en-/disable widget
.changeProperty (property, newValue) : a very powerful function, 
allowing to change the attributes of a widget dynamically. Will 
likely not work in many apps (or not soon, at least).