Home > R > Creating GUIs in R with gWidgets

Creating GUIs in R with gWidgets

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)
About these ads
Tags: , ,
  1. jverzani
    7th October, 2010 at 12:39 pm | #1

    Nicely illustrated Richie. Just want to clarify a bit about the different flavors of gWidgets: gWidgetsrJava is more or less not being maintained (it never had any demand for it); gWidgetsQt is just on r-forge and relies on qtbase which is also just on r-forge, and at this time isn’t working under windows, but once that is all ironed out gWidgetsQt should be on par with gWidgetsRGtk2 one. Finally, gWidgetsWWW isn’t quite the same, but close, and not only runs under RApache, will also run locally through the R help page server. Which means it doesn’t need to have a toolkit installed such as tcltk, RGtk2 or qtbase. (However, it is still in need of some users and the accompanying feedback to iron out issues.)

  2. Paul Prew
    7th October, 2010 at 15:40 pm | #2

    Richie, interesting post, even to a guy like me who has no programming skills. Which brings me to my question. I followed your last instruction concerning the generic widget function using “rpart”, a function w/in the rpart package for recursive partitioning. It seemed to work ( I want to say ‘compile’ from back in the day when they made me learn Fortan 77 – as in 1977). But how does the GUI get invoked? I don’t see anything in the documentation for ggenericwidget. I feebly tried the commands below. Thanks for any insight you can provide. Regards, Paul

    > rpart.widget()
    Error: could not find function “rpart.widget”
    > rpart.widget
    guiWidget of type: gGenericWidgetANY for toolkit: guiWidgetsToolkitRGtk2

    • 8th October, 2010 at 15:24 pm | #3

      Glad you liked the post, Paul. How the GUI gets invoked is a technical question best answered by John Verzani – to me it is more or less indistinguishable from magic. Having said that I think I can at least partly answer the rest of your question.

      When you create your rpart gui with the command
      rpart.widget <- ggenericwidget(rpart)
      then the window should appear automatically. If you close this window (by clicking the cross in the top right), then the variable rpart.widget remains in your workspace but AFAIK there is no way to view the window again without calling ggenericwidget again.

      For regular windows (as created bygwindow), you can show/hide them by calling visible(my_window) <- TRUE/FALSE but this functionality hasn’t been extended to ggenericwidgets yet.

      Note that some text editors regain focus after sending code to R, which can hide the GUI you’ve just created. Tinn-R does this by default, but you can turn it off via Options -> Applications -> R -> RGui -> Options -> Focus. And yes, it did take me a long time to find that.

  3. Paul Prew
    8th October, 2010 at 16:17 pm | #4

    Thanks, Richie. I was running the GUI RCommander, so I shut it off before using the ggenericwidget command. Still couldn’t get the window to appear, though. No beig deal, I was just playing around with it and don’t have any need really to work through the error. I’d be curious if it’s a really obvious fix, though. thanks, Paul

    > rpart.gwidget lmwidget <- ggenericwidget(lm)
    Error in objects$Name : $ operator is invalid for atomic vectors

    • 8th October, 2010 at 18:30 pm | #5

      rpart.gwidget lmwidget <- ggenericwidget(lm)

      In this line you have two things on the left hand side of the assignment, which is presumably a typo.

      $ operator is invalid for atomic vectors

      This error message happens when you try to access vectors like they were lists. Compare


      l <- list(foo = 1, bar = 2, baz = 3)
      v <- c(foo = 1, bar = 2, baz = 3)
      l["foo"]
      v["foo"]
      l$foo
      v$foo

      It’s hard to know what caused the error without a lot more context.

  4. jverzani
    8th October, 2010 at 20:20 pm | #6

    For invocation it should open,but for some reason doesn’t. I’ll have to check. In the meantime, you can work around this by passing in a top-level window for a container:

    w <- gwindow("rpart example")
    rp <- ggenericwidget(rpart, container=w)

    –J

  5. Paul Prew
    8th October, 2010 at 22:18 pm | #7

    John, the container worked, thanks for the fix. Richie, thank you for your help. regards, Paul

  6. 19th October, 2010 at 18:00 pm | #8

    this is awesomely terrific

    • 15th September, 2012 at 15:29 pm | #9

      That\’s a subtle way of tnhiking about it.

  1. 20th August, 2011 at 23:26 pm | #1
  2. 20th February, 2012 at 22:47 pm | #2
  3. 20th July, 2012 at 14:47 pm | #3
  4. 29th April, 2013 at 8:49 am | #4

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 160 other followers

%d bloggers like this: