[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).