Callback functions for GUI widgets

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.

About these ads

5 Responses to Callback functions for GUI widgets

  1. Ben Escoto says:

    I agree it seems weird to create multi-line anonymous functions. In my head I have the name “F” reserved as a temporary function name, and just define those functions normally, and then just pass them F. Easier to read and you don’t bloat your global namespace too badly.

    Also if you function is just one line you don’t need the {} braces like in your first example.

    I haven’t seen your RxR package until now; from the description it looks interesting and hits a real need. You may want to contact actuary Dom Yarnell if you’re not aware of his work. He is also working on automatic formatting/processes of reinsurance submission data.

    • PirateGrunt says:

      Ben, Dom’s work is the impetus for creating RxR. (I’ve just added a proper attribution to the package ReadMe.) He’s miles ahead of me and will be finished long before I get round to tidying up RxR, but I wanted to throw something R-based into the public domain (or the bit of the public domain that follows my activities on LinkedIn and GitHub). I’m delighted that Dom took the initiative to think about standardizing a common activity. I can understand why he chose Excel as a platform, but I’d love to see actuaries moving towards using a more flexible, universal approach. If nothing else, it’s not straightforward to use crowd-sourcing to develop an Excel tool (so far as I’m aware, anyway). In my view, if something is going to be a standard, then it has to be fully open. Meanwhile, though, Dom has done a good thing and I fully intend to exploit his hard work in whatever way possible.

      • Ben Escoto says:

        Sounds good, it will be great nice if you two can cooperate or at least interoperate in some way. It’ll be an uphill battle getting people to use it but EBDEx and RxR have tons of potential and it’ll be wonderful if they catch on.

  2. jverzani says:

    Nice post. Yes, there is a balance to be made between readability (and maintainability) and convenience. I find I too often write the callbacks as anonymous functions. It is how I think when I code, but usually when I go back and tidy up the code out they come if they are more than a line or two. (Guess i didn’t tidy the code in the book as well as I could have.) This can be pushed further than you do using Reference classes where the methods and widgets are bound together. This style seems more manageable than putting all the code into a monolithic function.Then the callbacks get quite simple, basically just a single method call. Qt’s slots are an extreme example here.

    • PirateGrunt says:

      John, thanks for the comment and thanks for writing the book. I’ve just started playing with windowed apps in R, but it’s quite a lot of fun. I have to confess that I’m not yet familiar with Reference classes. I’m slowly getting up to speed on S4 classes, but will check them out.

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 293 other followers

%d bloggers like this: