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.
我正在使用gWidgets在R中制作GUI。到目前为止,我一直在通过全球环境将值从一个窗口传递到另一个窗口。使用全局环境很容易实现,但并不理想。一个问题是R CMD检查抱怨缺少可见的全局变量绑定。
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.
作为这个问题的解决方案,几个R程序员已经提到了引用类。但是要理解引用类在这种情况下如何工作,有一个简单的例子真的很有帮助。
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.
让我给一个愚蠢的GUI工作。当用户点击第一个窗口的按钮时,它将模型m放在全局环境中。第二个按钮从全局环境中获取m并给出输出。当您再次按下第一个按钮时,它将创建一个新模型m并更改第二个按钮的输出。如果关闭第一个窗口,第二个窗口中的按钮仍然有效,因为m在全局环境中。
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?
如何在此示例中使用引用类?
2 个解决方案
#1
2
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.
调用setRefClass,并将每个窗口小部件和数据值包含为字段。小部件的类型应为ANY。在initialize方法中初始化这些小部件,并将功能外包给其他方法。创建一个函数来包装类的创建。
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))
}
#2
2
Richie's answer is one way to do it. It gives you a single object (the instance returned by generate_silly_gui
that you can use to manipulate the model and the widgets used for the GUI. A good approach. The following is simpler, it just does the model and is simply a slight deviation from the code in the question. The use of reference classes here is more structured way of using an environment:
里奇的答案是一种方法。它为您提供了一个单独的对象(generate_silly_gui返回的实例,您可以使用它来操作模型和用于GUI的小部件。这是一个很好的方法。以下更简单,它只是模型而且只是略微偏离问题中的代码。这里使用引用类是使用环境的更结构化的方式:
OurModel <- setRefClass("OurModel",
fields="m")
## a global
model_instance = OurModel$new(m=NULL)
Then just replace .GlobalEnv$m
with model_instance$m
in your code and run.
然后在代码中用model_instance $ m替换.GlobalEnv $ m并运行。
Using a reference class allows you do things like add getter's and setter's that also do other things and pushes you towards the model-view-controller style. The objectSignals
package goes in that direction.
使用引用类可以执行诸如添加getter和setter之类的操作,这些操作也会执行其他操作并将您推向模型 - 视图 - 控制器样式。 objectSignals包就是朝这个方向发展的。
If your GUI gets more complicated you might want to decouple the two approaches.
如果您的GUI变得更复杂,您可能希望将这两种方法分离。
#1
2
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.
调用setRefClass,并将每个窗口小部件和数据值包含为字段。小部件的类型应为ANY。在initialize方法中初始化这些小部件,并将功能外包给其他方法。创建一个函数来包装类的创建。
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))
}
#2
2
Richie's answer is one way to do it. It gives you a single object (the instance returned by generate_silly_gui
that you can use to manipulate the model and the widgets used for the GUI. A good approach. The following is simpler, it just does the model and is simply a slight deviation from the code in the question. The use of reference classes here is more structured way of using an environment:
里奇的答案是一种方法。它为您提供了一个单独的对象(generate_silly_gui返回的实例,您可以使用它来操作模型和用于GUI的小部件。这是一个很好的方法。以下更简单,它只是模型而且只是略微偏离问题中的代码。这里使用引用类是使用环境的更结构化的方式:
OurModel <- setRefClass("OurModel",
fields="m")
## a global
model_instance = OurModel$new(m=NULL)
Then just replace .GlobalEnv$m
with model_instance$m
in your code and run.
然后在代码中用model_instance $ m替换.GlobalEnv $ m并运行。
Using a reference class allows you do things like add getter's and setter's that also do other things and pushes you towards the model-view-controller style. The objectSignals
package goes in that direction.
使用引用类可以执行诸如添加getter和setter之类的操作,这些操作也会执行其他操作并将您推向模型 - 视图 - 控制器样式。 objectSignals包就是朝这个方向发展的。
If your GUI gets more complicated you might want to decouple the two approaches.
如果您的GUI变得更复杂,您可能希望将这两种方法分离。