Callback functions for GUI widgets

March 18, 2013
By

(This article was first published on PirateGrunt » R, and kindly contributed to R-bloggers)

Of all the things I dislike about R, one of the biggest is the fact that you can declare a function within the list of arguments to another function. I’ve gotten over it for very minor operations needed by things like lapply, but it can drive me bonkers elsewhere. One such instance is writing an event handler using the gwidgets package. Here’s an example, inspired by the example in “Programming Graphical User Interfaces in R“:

# Create a basic window with an output section
require(gWidgets)
options(guiToolkit = RGtk2)
window = gwindow("File search", visible = FALSE)
paned = gpanedgroup(cont = window)
frmOutput = gframe("Output: ", cont=paned, horizontal = FALSE)
txtOutput = gtext("", cont = frmOutput, expand = TRUE)
size(txtOutput) = c(350, 200)

# Create a button to open a .csv file
btnImportFile = gfilebrowse(text = "Select a file ", quote = FALSE, type = "open", cont = container, filter = "*.csv")
addHandlerChanged(btnImportFile, handler = function(h, ...){
    svalue(txtOutput) = svalue(h$obj)    
  })

OK, that was a fairly tame handler. All that we’re doing is placing the name of the file into the output area. What I’d like to do is use the output to show the names of columns contained in the file. Here’s what that would look like:

btnDescribeColumns = gbutton(text = "Describe columns", cont = container)
addHandlerChanged(btnDescribeColumns, handler = function(h, ...){
   filename = svalue(txtOutput)
   if( filename == ""){
     svalue(txtOutput) = "File not found"
    } else {
     df = read.csv(filename, header = TRUE)
     svalue(txtOutput) = names(df)
    }
})

That’s not too dreadful in isolation, but the event handler sits in the same function where I’ve defined another event handler. I’m now defining two functions inside a third function. For any rich interface, this will quickly get complex. Making changes to a specific handler means trolling through one giant, monolithic function. (Yes, yes, I could just search, but the aesthetics of my code are still poor.)

How about this instead? I code a function to create the widget and the handler is defined there.

AddBtnDescribeColumns = function(container, txtOutput){
  require(gWidgets)
  btnDescribeColumns = gbutton(text = "Describe columns", cont = container)
  
   addHandlerChanged(btnDescribeColumns, handler = function(h, ...){
     filename = svalue(txtOutput)
     if( filename == ""){
       svalue(txtOutput) = "File not found"
      } else {
       df = read.csv(filename, header = TRUE)
       svalue(txtOutput) = names(df)
      }
 })
 
  return (btnDescribeColumns)
}

With a similar approach to the file open button (not shown, but available on Github), the main function for my dialog box is beginning to look fairly readable. Note that I must declare the output box before the buttons, so that it may be passed to the button constructors.

Main = function(WhichToolkit = "RGtk2"){
  require(gWidgets)
  options(guiToolkit = WhichToolkit)
  
  window = gwindow("File search", visible = FALSE)
  
  paned = gpanedgroup(cont = window)
  
  group = ggroup(cont = paned, horizontal = FALSE)
  frmOutput = gframe("Output: ", cont=paned, horizontal = FALSE)
  
  txtOutput = gtext("", cont = frmOutput, expand = TRUE)
  
  size(txtOutput) = c(350, 200)
  
  AddBtnImportFile(group, txtOutput)
  
  AddBtnDescribeColumns(group, txtOutput)
  
  visible(window) = TRUE
}

Another nice aspect of this construct is that I can declare multiple copies of the same button and place them wherever I like. They’ll all have the same event handler, which I only need to code once.

I’ve been working with gWidgets for a sum total of about three hours, so there may be something obvious I’ve missed. If so, please feel free to comment.


To leave a comment for the author, please follow the link and comment on his blog: PirateGrunt » R.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.