I am making a GUI in R using gWidgets. Until now I have been passing values from one window to another via the global environment. Using the global environment is simple to implement but not ideal. One problem is that R CMD check
complains about lacking visible bindings for global variables.
As a solution to this problem, reference classes have been mentioned by several R programmers. But to understand how reference classes would work in this context, it would really help to have a simple example.
Let me give a silly GUI to work with. When the user hits the button of the first window, it puts the model m
in the global environment. The second button gets m
from the global environment and gives an output. When you hit the first button again, it will make a new model m
and change the output of the second button. If you close the first window, the button in the second window will still work, because m
is in the global environment.
library(gWidgets)
options(guiToolkit = "tcltk")
h1 <- function(h, ...){
d1 <- data.frame(x=runif(10), y=runif(10))
.GlobalEnv$m <- lm(x ~ y, data=d1)
}
g1 <- gbutton("1. Make model",
container=gwindow(), handler=h1)
h2 <- function(h, ...){
d2 <- data.frame(y=(1:10)/10)
p <- predict(.GlobalEnv$m, newdata=d2)
print(p)
}
g2 <- gbutton("2. Make prediction",
container=gwindow(), handler=h2)
How can I use reference classes in this example?
Call setRefClass
, and include each widget and data value as a field. Widgets should have type ANY
. Initialize those widgets in the initialize
method, and outsource functionality to other methods. Create a function to wrap the creation of the class.
silly_gui_generator <- setRefClass(
"SillyGui",
fields = list(
#widgets
win1 = "ANY",
win2 = "ANY",
button1 = "ANY",
button2 = "ANY",
#data
modelData = "data.frame",
predictionData = "data.frame",
model = "lm"
),
methods = list(
initialize = function(modelData = NULL)
{
if(is.null(modelData))
{
modelData <<- data.frame(x = runif(10), y = runif(10))
}
win1 <<- gwindow(visible = FALSE)
win2 <<- gwindow(visible = FALSE)
button1 <<- gbutton(
"1. Make model",
container = win1,
handler = function(h, ...)
{
makeModel()
}
)
button2 <<- gbutton(
"2. Make prediction",
container = win2,
handler = function(h, ...)
{
print(predictModel())
}
)
visible(win1) <- TRUE
visible(win2) <- TRUE
},
makeModel = function()
{
model <<- lm(x ~ y, data = modelData)
},
predictModel = function()
{
predictionData <<- data.frame(y = (1:10) / 10)
predict(model, newdata = predictionData)
}
)
)
generate_silly_gui <- function(modelData = NULL)
{
invisible(silly_gui_generator$new(modelData = modelData))
}