run.dialog: Simple Dialog Maker package for RGtk2

Description Usage Arguments Details Examples

View source: R/SimpleDialogMaker.r

Description

An RGtk2/gWidgetsRGtk2 based package for quick and easy dialog development.

Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
run.dialog(func, 
   pos = -1,
   envir = as.environment(pos),
   dlg.list = tryCatch(
   get(paste(func.name, "dialog", sep="."), 
   envir=envir), 
   error =function(e) NULL),   
   var.browser=NULL,
   parent.window=NULL, 
   auto.assign=TRUE, 
   do.long.running=FALSE,
   OK_handler= default.handler,
   output.name.rule = "append",
   output.name = NULL,
   do.logging = TRUE,
   log.handler = NULL,
   user.args = NULL,
   ...)

Arguments

func

Function to be run when "OK" is pressed

pos

where to look for the dialog markup (see the details section)

envir

alternative specification of environment

dlg.list

Optional list containing dialog markup

var.browser

An optional gWidgets gvarbrowser

parent.window

An optional parent window to set modal

auto.assign

Assign the function return value, if it exists, to the global environment with an automatically assigned name

output.name.rule

Rule for assigning output name

do.long.running

Use long running task routine for Windows

OK_handler

Function to pass do.call(func, args to)

output.name

Name of return value; overrides auto.assign and output.name.rule

do.logging

Send the deparsed function call to an external function?

log.handler

Function to take the deparsed function call

user.args

Optional values to pass to the dialog which overrule those in dialog

...

Additional arguments

Details

This package is intended to assist rapid development of simple dialogs to serve as front-ends to existing R functions. It is a riff off (or rip off) of ideas from John Verzani's traitr toolkit where flexibility is sacrificed for ease of use. Complex layouts are not supported; this package is for getting things working quickly, not necessarily looking beautiful.

To create a working dialog that calls a function, a corresponding markup list is needed specifying how each argument of the function should look on the screen - in other words, what widgets to use. Then run.dialog() can be called on the function.

Calling run.dialog()

If your function is called MyFunction and the markup list is called MyFunction.dialog or .MyFunction.dialog then calling run.dialog(MyFunction) will attempt to locate the corresponding markup list and create and show the dialog. When the user presses "OK" the dialog will call the function with the specified arguments.

The dialog will be modal and centered on the gtkWindow parent.window if this is specified. The R main loop will be blocked until the dialog is destroyed.

Alternatively, run.dialog(MyFunction, dlg.list=MyMarkupList) will explicitly specify the dialog markup list for MyFunction.

Return Values
run.dialog() returns a list containing retval and args, where retval is the return value of the function call and args is a list containing the named arguments specified from the dialog.

Dialog Markup Specification

For a function MyFunction with signature

MyFunction = function(arg1, arg2, ...)

the dialog markup list is specified by

MyFunction.dialog = list(
dialog_markup = value0, ...,
arg1.dialogItem1 = value_of_arg1, markup1 = value1, ...,
arg2.dialogItem2 = value_of_arg2, markup2 = value2, ...,
...
)

The only necessary items in the markup list are those of form

argn.dialogItem = value_of_argn

The name arg1.dialogItem in the MyFunction.dialog markup list tells run.dialog() to display a widget of type dialogItem corresponding to arg1. This widget is initialized to value_of_arg1 and passes this value as the arg1 parameter to MyFunction when "OK" is clicked.

Optional markup elements that affect the entire dialog are specified by dialog_markup items before the first arg.dialogItem, and markup elements affecting individual dialogItems are specified between them and the next dialogItem (or in the case of the last item, the end of the list).

dialogItem can be any one of the following: integerItem, numericItem, rangeItem, stringItem, trueFalseItem, choiceItem, radiobuttonItem, objectItem, dataframeItem, fileItem, variableSelectorItem, listItem, buttonItem. Each of these Items displays a different commonly used dialog GUI widget which are listed below.

Most widgets are also set into a small rectangular frame which contains a label. By default the label is set to the name of the corresponding function argument, but this can be set using the label markup.

The widgets are laid out very simply within the dialog GUI, set top to bottom in a column. If the column layout is told to break using the BREAK=TRUE markup, another column is added to the left hand side and the widgets after that point are laid out into the new column. An optional label for the entire dialog can be added to the top over all columns.

Markup

Markups affecting the entire dialog
These are set before the first arg.dialogItem

label = "This GUI does x" sets the label centered over all columns.

title = "My Function" sets the title of the dialog window.

keep.open = TRUE tells the dialog to stay open after "OK" is pressed.
This can be useful if your function plots something, and the user is likely to want to try different plot settings.

show.progress = TRUE tells run.dialog that the function should display a dialog while it is executing to allow monitoring of progress and cancellation. This is based on Felix Andrews' interrupt.c from playwith. If this markup is present then when "OK" is pressed a small modal Progress dialog will appear with a cancel button. When this button is pressed, an interrupt signal is sent and the Progress dialog is destroyed and the main dialog is destroyed if appropriate. This is not guaranteed to stop all running R processes. Moreover, if your function arguments contain progressbar or progresslabel, then run.dialog will pass your function a gtkProgressBar and a gtkLabel respectively, to allow user monitoring of progress via progressbar$setFraction() or progressbar$setText(). See ?gtkProgressBar and ?gtkLabel.
Note 1: if your function updates the progress bar or progress label, it should give these arguments default NULL values and check they are not missing before using them, otherwise it will break if run without a dialog.
Note 2: if your dialog markup (as opposed to your function) contains progressbar or progresslabel arguments, then run.dialog will not pass those widgets to your function automatically.

long.running = TRUE tells run.dialog that the function will run for a long time before returning. This is fairly experimental, and doesn't work anywhere but Windows yet. In Windows, it will do the following things:
1. Save the R session to the current working directory.
2. Create a batch file containing a command to load the R session, delete the session file, carry out the function call, and a command to save the session after the function returns.
3. Spawn a separate R process and call exec(R CMD BATCH batch_file) to perform the function, and record that process's process ID (PID).
4. Show a modal window over the dialog with an endlessly repeating progress bar, and a Cancel button. If the Cancel button is pressed, it will kill the spawned R process using taskkill PID and close the progress window.
5. Monitor periodically for the saved session file to appear in the working directory, and then load the session file and close the progress window.

Dialog markups that work for all items

arg_name.dialogItem = arg_value sets the dialog item value, OR,
arg_name.dialogItem = substitute(expression)
sets the value of the widget to the evaluated expression.

label = "This widget specifies x" sets the label for the widget frame.

tooltip = "the tooltip string" sets a tooltip over the dialog item

BREAK = TRUE stops adding widgets to the end of the column and starts a new column.

indent= 10 adds a 10 pixel indentation to the left hand side of the widget frame which can be useful to show hierarchical organization. signal = c(signal_type, signal_function, widget1, widget2, ..., user_data=NULL) specifies a widget signal (see below). More than one signal can be set for each widget.

signal.at.startup = FALSE will NOT send the "default" signal from this dialog element when the dialog is initialized.

set.sensitive=FALSE will gray out the corresponding widget

suppress=TRUE will prevent the widget's value from being passed to the function. This is useful for "dummy" widgets that contain lists of values to choose from.

visible=FALSE will just not display the widget. Its value will still be passed to the function and it can be called normally by signaling functions.

Dialog Items

This is a list of the currently available widgets that can be specified using markup, together with additional markup that can be used to specify their behavior.

As well as that, there are set.value() and get.value() methods that work on most widgets, and also signals which they can emit (see Signal Handling). set.value() takes an optional propagate argument which is TRUE by default. If this is set to FALSE, then the widget won't emit its signal.

This list specifies what the set.value and get.value methods return for each widget and what behavior leads to each widget emitting a signal.

stringItem: An entry box which can take text input.
x.stringItem = "hi there"
Set and Get Calls:
get.value(item) returns the string value.
Signals:
"default" is emitted when Enter is pressed.

numericItem: A stringItem which coerces the value to a number.
x.numericItem = 1.3
Set and Get Calls:
get.value(item) returns the numeric value.

rangeItem: A horizontal slider for returning a numeric value.
x.rangeItem = c(value=1, from=0, to=2, by=0.1)
The value sets the value, min, max and step.
Set and Get Calls:
get.value(item) returns the numeric value.
Signals:
"default" is emitted when the range is altered.

integerItem: a spin button which takes an integer value.
z.integerItem = 1
z.integerItem = c(value=1, from=0, to=10, by=1)
Set and Get Calls:
get.value(item) returns the integer value.
The value can be specified in two ways, first using a single number,
second with a named vector containing the value and the min, max and step of the spin button.

trueFalseItem: A check box.
an.option.trueFalseItem = TRUE
This widget will not be put in a frame but rather have its label set to any label markup.
Set and Get Calls:
get.value(item) returns the check box state.
set.value(item, value, propagate=TRUE) sets the item to the logical value.
Signals:
"default" is emitted when the check box is changed.

choiceItem: A combo box allowing selection of a value.
x.choiceItem = c(choice1, value=choice2, choice3, ...)
where "value=" sets the initial choice.
Additional markup:
by.index=T will return the selected indices rather than their strings.
item.labels = c("A", "B", "C") will display the choices as these labels rather than the values given. The item.labels vector must be the same length as the values vector if it exists.
Set and Get Calls:
get.value(item) returns the selected choice.
get.value(item, selected=F) returns all of the choices in the choiceItem.
set.value(item, value, propagate=TRUE) sets the available choices in the choiceItem to the vector (or possibly NULL) value.
Signals:
"default" is emitted when the selection is changed. Note that popping up the list box will not signal.

radiobuttonItem: Identically specified to choiceItem but shows radio buttons rather than a combo box. Better for small numbers of choices or longer choice names. Also, the choices in radiobuttonItem cannot be changed using set.value(), unlike choiceItem.
Markup for radiobuttonItem is the same as choiceItem.
Set and Get Calls:
get.value(item) returns the selected choice.
set.value(item, value, propagate=TRUE) sets the item to the value and throws an error if it is not one of the available choices.
Signals:
"default" is emitted when the selection is changed.
fileItem: a label and a button. When the button is pressed a file name can be selected or typed in.
fileName.fileItem = "C:/R/test.R"
Additional markup:
extension = "xls" opens *.xls
type = "open", "save", "selectdir" gives options to open, save or select directories
multiple = TRUE allows multiple file selection (not compatible with type = "selectdir")
Set and Get Calls:
get.value(item) returns a string or vector of strings corresponding to the selected files or directories.
No set.value() call yet.
Signals:
"default" is emitted when any file is selected.

objectItem: a label and a button. When the button is pressed a chooser dialog opens allowing selection of objects from the global environment (using a gWidgets gvarbrowser) on either double click or select and close.
Its value is a string containing the name of the default object, otherwise NULL or "".
data.objectItem = "iris"
Additional markup:
data.types ="list" specifies a list of data types. If your selected object from the chooser doesn't match that type, it won't change the selected value.
as.character =TRUE means that the name of the selected dataset rather than the symbol will get passed back to the function.
Set and Get Calls:
get.value(item) returns the selected object name(the string) rather than the object itself. This is slightly inconsistent with objectItem's default returned value to the function which is the object symbol. No set.value() call yet.
Signals:
"default" is emitted when any object is selected.

dataframeItem: identical to objectItem with default data.type markup of c("data.frame", "matrix"). Also, if a gWidgetsRGtk2 gvarbrowser is specified in run.dialog(..., var.browser), the FIRST dataframeItem in the dialog will have its value set to any dataframe or array selected in the gvarbrowser.

variableSelectorItem: a widget which displays a list with checkboxes to choose a subset of strings. The check box column header is labeled "Select All" and clicking it will alternate between selecting and deselecting all items.

myColumns.variableSelectorItem = letters[1:5]
myColumns.variableSelectorItem = substitute(colnames(iris))
A variableSelectorItem will pass to the function a (possibly zero-length) vector containing the selected strings.
Set and Get Calls:
get.value(item) returns the selected values.
set.value(item, letters[1:5], propagate=TRUE) sets the values in the item.
Signals:
"default" is emitted when the check boxes are altered anywhere.

listItem: an widget containing a simple list box set in a panel with buttons labeled with right and left arrows next to it.
The list allows multiple selections.
myColumns.listItem = substitute(colnames(iris))
nothingYet.listItem = NULL
Additional markup:
show.arrows = FALSE turns displaying the arrow buttons off.
max.items = 2 sets the maximum number of items the listItem can contain to 2.
Set and Get Calls:
get.value(item, select=TRUE) returns the selected values only.
get.value(item, select=FALSE) returns all values.
set.value(item, letters[1:5], propagate=TRUE) sets the values in the list item.
Signals:
"default" is emitted when the list is clicked anywhere.
"add" is emitted when the right-arrow button is clicked.
"subtract" is emitted when the left-arrow button is clicked.

buttonItem: a frameless button.
button.buttonItem = "Click Me"
Set and Get Calls:
None, yet.
Signals:
"clicked" is emitted when the button is clicked.

Dot Arguments
If a markup item called ....stringItem is present, run.dialog() will display it as a stringItem widget with an "Additional Arguments" label.
When the dialog "OK" is clicked run.dialog() will run eval(parse(text=get.value(text))) on whatever text is put in that markup, then place any additional items as additional arguments to the function.

Signaling Signal handling can be specified to support some commonly required function dialog behaviors. For example, deselecting a checkbox might set another widget to insensitive (grayed out), or some items might be moved between list boxes.

Signaling is specified using the signaling markup, set after a dialog item: signal = c(signal_type, signal_function, widget1_arg, widget2_arg, ..., user_data=NULL) containing the following specifications:

signal_type is a string, for example "default", "clicked", etc.
All the dialogItems will broadcast a signal called "default" whenever something happens to them corresponding to a user interaction.
This is usually what you're going to want to connect a signal to, but there are exceptions.
signal_function is a function or a name of a function visible in the environment run.dialog is called from. It has this signature:

signal_function(item, widget1, widget2, ..., user_data=NULL)
item is the widget that sent the signal;
widget1, widget2 are GtkWidget objects specified by ;
user_data is the optionally specified user data

widget1_arg, widget2_arg, ... are the function argument strings corresponding to the widgets being passed to the signal_function.

user_data is optional additional data to send to the function.

Within your signal_function, you're going to want to be able to get and set the values your widgets take.

You can do this using the get.value(widget) and set.value(widget) calls. In some cases these calls can take additional arguments. See the DensityHistogram example below.

Actually, we could use the built-in function toggle.sensitive which does the same thing (the commented-out #signal line). In this case, passing a string as the signaling function will make run.dialog search for a function with this name.

One further question is, how does run.dialog set the rangeItem to be insensitive the first time it opens? By default, run.dialog() calls the "default" signal from every one of the widgets when it opens, unless either
(a) they are set to being insensitive or
(b) the widget signal.on.startup markup is set to FALSE.

Because the default signal from some widgets can set others to being insensitive, this allows reasonable behavior for hierarchically grouped toggled sensitive widgets. On the other hand, it may still be desirable to prevent widgets firing their signals when the dialog is starting, for example if you have many widgets performing some action like refreshing a list. In this case, setting signal.on.startup to FALSE for most of them should fix that problem.

Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
## Not run: 
 # A simple histogram plotting example:
  SimpleHistogram = function(N) hist(rnorm(N))

  SimpleHistogram.dialog = list(label = "A density-plotting histogram dialog",
   N.integerItem = 50, label = "Value of N")

  run.dialog(SimpleHistogram) 
 # end example
 
 # example 2
   # Function to plot a histogram, optionally adding a density plot with
   # adjustable bandwidth. If add.density is false, gray out the bandwidth range.

   # This is what's going on in the "signal" markup from the add.density widget:
     # We specify the "default" signal from the add.density trueFalseItem
     # Then specify the signal function, which takes the add.density 
     # trueFalseItem as its first argument and the bw.rangeItem 
     # as its second. In the function, we first of all
     # get the value of the trueFalseItem (which is TRUE or FALSE)
     # and call gtkWidgetSetSensitive on bw.widget with this value
     # (see the RGtk Help for gtkWidget)
     # The last element of the signal markup is the string "bw"
     # which run.dialog() replaces with the corresponding bw.rangeItem and
     # passes to the called signal function as its second argument.

  DensityHistogram = function(N, add.density, bw=NULL)
  {
     x <- rnorm(N)
     hist(x, probability=add.density)
     if(add.density) points(density(x, bw=bw), type = "l")
  }

  DensityHistogram.dialog = list(
   #keep.open = TRUE, # uncomment to keep the dialog open
   label = "A density-plotting histogram dialog",
   N.integerItem = c(value=100, from=10, to=1000, by=10), label = "Value of X",
   add.density.trueFalseItem = FALSE, label = "Add Density Plot?",
     signal = c("default", 
       function(item, bw.widget) bw.widget$setSensitive(get.value(item)), "bw"),
    # signal = c("default", "toggle.sensitive", "bw"), # using the built-in
   bw.rangeItem = c(value=0.3, from=0, to=2, by=0.05), label = "Bandwidth")

  run.dialog(DensityHistogram)
  # end example 2
  
    # Angle display demo, from Graham Williams
  demo(MakeAngle)
  
    # Cancel dialog demo
  demo(ProgressBar)


## End(Not run)

RGtk2Extras documentation built on May 29, 2017, 11:19 a.m.