Archive

Posts Tagged ‘gui’

Look ma! No typing! Autorunning code on R startup

20th July, 2012 6 comments

Regular readers may know that I often make R-based GUIs. They’re great for giving non-technical users safe and easy access to statistical models. The safety comes from the restrictions of a GUI: you can limit what the users does more easily than with a command line, helping to reduce the number of opportunities for bad science. My tool of choice for GUI building is John Verzani’s set of gWidgets packages; see my introduction and comparison with Deducer.

Since the target audience is non-technical, an important aim is to reduce the amount of typing at the R command prompt. Typically, I wrap the GUI into package, and have a single function call to load the GUI, so the users will have to start R, then type something like

library(myGui)
gui <- runTheGui()

#Here's an example GUI to play with now
runTheGui <- function()
{
  win <- gwindow("Test", visible = FALSE)
  rad <- gradio(letters[1:4], cont = win)
  visible(win) <- TRUE
  focus(win)
  list(win = win, rad = rad)
}

Typing two lines isn’t too onerous, but the ideal situation would involve no typing at all. That is, you double click a shortcut that opens R and then the GUI. With a little help from the internet, I have two solutions. Which one is best depends upon your setup.

The first solution was suggested to me by my collaborators Simon and Mark over at Drunks & Lampposts, who got it from Greg Snow. I’ve refined the technique to make it simpler.

There are two tricks involved. Firstly, when R (at least R GUI; Eclipse, RStudio, emacs, etc. may require configuration) starts up, by default it will run a function named .First, if that function exists. So our first task is to put those previous lines of code inside that function.

.First <- function()
{
  library(myGui)
  gui <<- runTheGui()
}

Then, we save that function into an R binary workspace file.

save(.First, file = "~/Desktop/runTheGui.RData")

The second trick is that (assuming your operating system has been configured correctly), double-clicking a .RData file will start R GUI, loading said .RData file, and running the contents of that .First function.

So all the user needs to do is double click the RData file, and the GUI will run.

This is exactly what we wanted, but it has a small drawback in that R GUI isn’t available on all platforms. Also, if you really don’t want users to type things, then you may not want R GUI at all. In that case, using Rscript (as suggested by Dirk Eddelbuettel) is a better solution. Rscript is a little bit like batch mode. It open R in a terminal, runs a script, then closes R again. So for this solution, we need to create a script. This time we don’t need to wrap the contents inside a function. We do need to add something to the end of the script to prevent R closing down once the script has run, such as a check that the window is still open. (Note that since the R console won’t be available this time, this solution isn’t that useful for non-GUI purposes.)

library(myGui)
gui <- runTheGui()
while(isExtant(gui$win)) Sys.sleep(1)

Now to get the GUI running, you create a shortcut to Rscript, with the script file as an argument. Change the path to R and to the script file as appropriate.

"%ProgramW6432%\R\R-2.15.1\bin\Rscript.exe" "path/to/your/script/runTheGui.R"

And voila! We have a GUI running in R, again from a simple, single double-click, and this time R closes itself down when the GUI closes.

Tags: , , ,

GUI building in R: gWidgets vs Deducer

20th February, 2012 5 comments

I’ve been a user (and fan) of gWidgets for a couple of years now for GUI building in R. (See my introduction to it here.) However, it’s always good to check out the competition so I’ve been playing around with Deducer to see how they compare.

R can access a number of GUI building frameworks including tcltk, GTK, qt, and Java, not to mention HTML. gWidgets’ big selling point is that is provides a high-level wrapper to all the R wrappers for each framework, so you can write code in a toolkit independent way. Switching between tcltk and GTK and qt won’t often be that useful, but if you think you might want to move from a desktop based GUI to a web app, it makes the transition easier. By contrast, Deducer based upon the rJava, and provides access to the Java Swing framework. It’s a slightly lower level library (which means you have to write more lines of code to achieve the same thing), but since you get full access to Swing, it’s a little more flexible. Deducer also has some features to integrate your GUIs with JGR, so if you use that for running R, it’s perhaps the most natural choice.

To test the two frameworks, I wrote a small GUI for running the Kolmogorov-Smirnoff test (that one of the ones for checking whether or not a variable seems to have been sampled from a particular distribution). Take a look at the code below to see the comparison. (Regular reader may notice I’ve switched from my usual under_casing to camelCasing. Both the frameworks use this style, so I thought I’d follow suit for cleanliness.)

First, here are some common variables (labels and the like).

#Some sample data to test against
x1 <- rnorm(100)
x2 <- runif(100)

#Widget labels
labelX <- "Variable name for data: "
labelY <- "Distribution to compare to: "
labelAlternative <- "One or two sided test?: "
labelP <- "The p-value is: "

#Choices for comboboxes
choicesAlternative <- eval(formals(ks.test)$alternative)
distributions <- c(
    normal = pnorm, 
    exponential = pexp,
    F = pf,
    "log-normal" = plnorm,
    "Student's t" = pt,
    uniform = punif
)

This is the gWidgets GUI

createKsTestGwidgets <- function()
{
  library(gWidgetstcltk)
  options(guiToolkit = "tcltk")
  win <- gwindow("KS Test, gWidgets edition", visible = FALSE)
  
  frmX <- gframe("x", container = win)
  lblX <- glabel(labelX, container = frmX)
  txtX <- gedit(container = frmX)
  
  frmY <- gframe("y", container = win)
  lblY <- glabel(labelY, container = frmY)
  cmbY <- gcombobox(names(distributions), container = frmY)
  
  frmAlternative <- gframe("alternative", container = win)
  lblAlternative <- glabel(labelAlternative, container = frmAlternative)
  cmbAlternative <- gcombobox(choicesAlternative, container = frmAlternative)
  
  btnCalc <- gbutton("Calculate", container = win,
      handler = function(h, ...)
      {
        x <- get(svalue(txtX), mode = "numeric")
        y <- distributions[[svalue(cmbY)]]
        alternative <- svalue(cmbAlternative)
        ans <- ks.test(x, y, alternative = alternative)
        svalue(txtP) <- format(ans$p.value, digits = 3)
      }
  )
  frmResults <- gframe("results", container = win)
  lblP <- glabel(labelP, container = frmResults)
  txtP <- gedit(container = frmResults)
  visible(win) <- TRUE
  
}

createKsTestGwidgets()

…and here’s the Deducer equivalent.

createKsTestDeducer <- function()
{
  library(Deducer)
  win <- new(RDialog)
  win$setSize(300L, 500L)
  win$setTitle("KS TEST, Deducer edition")
  
  JLabel <- J("javax.swing.JLabel")
  lblX <- new(JLabel, labelX)
  addComponent(win, lblX, 1, 1000, 50, 1, rightType = "REL")
  txtX <- new(TextAreaWidget, "x")
  addComponent(win, txtX, 51, 1000, 150, 1, rightType = "REL")
  
  lblY <- new(JLabel, labelY)
  addComponent(win, lblY, 151, 1000, 200, 1, rightType = "REL")
  
  cmbY <- new(ComboBoxWidget, names(distributions))
  cmbY$setDefaultModel(names(distributions)[1])
  addComponent(win, cmbY, 201, 1000, 300, 1, rightType = "REL")
  
  lblAlternative <- new(JLabel, labelAlternative)
  addComponent(win, lblAlternative, 301, 1000, 400, 1, rightType = "REL")
  
  cmbAlternative <- new(ComboBoxWidget, choicesAlternative)
  cmbAlternative$setDefaultModel(choicesAlternative[1])
  addComponent(win, cmbAlternative, 401, 1000, 500, 1, rightType = "REL")
  
  JButton <- J("javax.swing.JButton")
  btnCalc <- new(JButton, "Calculate")
  addComponent(win, btnCalc, 501, 1000, 601, 1, rightType = "REL")
  ActionListener <- J("org.rosuda.deducer.widgets.event.RActionListener")
  listener <- new(ActionListener)
  calculationHandler <- function(cmd, ActionEvent)
  {
    x <- get(txtX$getText())
    y <- distributions[[cmbY$getModel()]]
    alternative <- cmbAlternative$getModel()
    ans <- ks.test(x, y, alternative = alternative)
    print(ans)
    txtP$setText(format(ans$p.value, digits = 3))
  }
  listener$setFunction(toJava(calculationHandler))
  btnCalc$addActionListener(listener)
  
  lblP <- new(JLabel, labelP)
  addComponent(win, lblP, 601, 1000, 650, 1, rightType = "REL")
  
  txtP <- new(TextAreaWidget, "results")
  addComponent(win, txtP, 651, 1000, 750, 1, rightType = "REL")
    
  win$run()
}

createKsTestDeducer()

Note that the Deducer example works perfectly under JGR, though I couldn’t get the button handler to fire when running it from eclipse. This is likely due to my inexperience with the toolkit rather than a fundamental problem with the framework. Many of the lines are more or less a one-to-one comparison, but Deducer requires you to explicitly specify positions of widgets, and is a little more verbose when you come to add event handling logic.

Either or these frameworks is suitable for the obvious use case of GUI building in R (rapid prototyping of front-ends for non technical users, and for teaching demos), so don’t sweat your decision too much.

Edit: Fixed variable name casing issues.

Creating GUIs in R with gWidgets

6th October, 2010 13 comments

The gWidgets framework is a way of creating graphical user interfaces in a toolkit independent way.  That means that you can choose between tcl/tk, Gtk, Java or Qt underneath the bonnet. There’s also a web-version based upon RApache and ExtJS. Since the code is the same in each case, you can change your mind and swap toolkits at a later date, without having to rewrite everything.  Different versions of the toolkit are in different states of development; Gtk is the most complete, but the tcl/tk and Java versions are usable. The Web version has had a recent rewrite, which I haven’t used so I can’t vouch for it’s status.  Finally, the Qt version is still experimental (and not yet available on CRAN). Personally, I use the tcl/tk version, since all the necessary components ship with the Windows edition of R.

The framework is  fairly high level, making it quick for prototyping user interfaces.  The drawback is that you don’t get quite as much control over the styling of your interface.  If you need finer control, you may prefer one of the lower level packages: RGtk2, tcltk or rJava.  In those cases, you will lose the toolkit independence.

To learn how gWidgets works, we’ll build a dialog box with controls to upload a tab delimited file. To begin, we load the necessary packages.

library(gWidgets)
library(gWidgetstcltk) #or gWidgetsGtk2 or gWidgetsrJava or gWidgetsWWW or gWidgetsQt

The textboxes and checkboxes and so forth that we need are known as widgets (hence “gWidgets”). They need to be contained inside a window, which we create using the function gwindow.

win <- gwindow("Tab delimited file upload example")

By default, the widgets will be stacked up vertically. We can create groups of widgets that are stacked horizontally with ggroup(which is a widget in itself). Notice that all widgets must specify their container; in this case it’s just the window.

grp_name <- ggroup(container = win)

A glabel is a widget that represents a text label. Notice that it is contained inside the group we just created.

lbl_data_frame_name <- glabel(
  "Variable to save data to: ",
  container = grp_name
)

A gedit is a single line textbox. (Not to be confused with a gtext, which is a multiline textbox.)

txt_data_frame_name <- gedit("dfr", container = grp_name)

Another horizontal group, for the upload button.

grp_upload <- ggroup(container = win)

For widgets that we want to respond to an action, we need to add a handler argument. This is always a function accepting a list as its first argument (named h by convention), and dots. The gbutton handler is called whenever the button is clicked. Don’t worry about the contents of the handler function for now; we’ll add them in a moment.

btn_upload <- gbutton(
  text      = "Upload tab delimited file",
  container = grp_upload,
  handler   = function(h, ...)
  {
    # TODO!
  }
)

Since tab delimited files can have decimal places represented as full-stops or commas (depending upon local conventions), we need a checkbox to choose between cases. We define a function to get the default value from the system locale settings. Conveniently, checkboxes have their own label built-in so we don’t need to create our own this time.

use_comma_for_decimal <- function()
{
  unname(Sys.localeconv()["decimal_point"] == ",")
}

chk_eurostyle <- gcheckbox(
  text      = "Use comma for decimal place",
  checked   = use_comma_for_decimal(),
  container = grp_upload
)

The last widget we’ll include is a status bar, so that users don’t have to refer back to the R main window for messages.

status_bar <- gstatusbar("", container = win)

Finally, here’s the content for the button handler. It creates a file open dialog box, which in turn has its own handler function. The action argument names the function to be applied to the file that is opened. The svalue function returns the “most useful thing” from a widget. For a checkbox, the svalue is whether or not it is checked. For a textbox or status bar, the svalue is its text. The filter argument populates the “Files of type” drop down list in the file open dialog.

function(h, ...)
{
  gfile(
    text    = "Upload tab delimited file",
    type    = "open",
    action  = ifelse(svalue(chk_eurostyle), "read.delim2", "read.delim"),
    handler = function(h, ...)
    {
      tryCatch(
        {
          data_frame_name <- make.names(svalue(txt_data_frame_name))
          the_data <- do.call(h$action, list(h$file))
          assign(data_frame_name, the_data, envir = globalenv())
          svalue(status_bar) <-
            paste(nrow(the_data), "records saved to variable", data_frame_name)
        },
        error = function(e) svalue(status_bar) <- "Could not upload data"
      )
    },
    filter = list(
      "Tab delimited" = list(patterns = c("*.txt","*.dlm","*.tab")),
      "All files" = list(patterns = c("*"))
    )
  )
}

And there we have it.

The file upload dialog box we created

If you’re feeling enthusiastic, see if you can adapt this to work with CSV files, or even better a general delimited file.

One last trick to finish the post: You can create a GUI interface to any function using ggenericwidget. Try

lmwidget <- ggenericwidget(lm)
Tags: , ,
Follow

Get every new post delivered to your Inbox.

Join 160 other followers