【问题标题】:Using R reference classes to pass values from one window to another in a GUI在 GUI 中使用 R 引用类将值从一个窗口传递到另一个窗口
【发布时间】:2013-06-28 00:17:53
【问题描述】:

我正在使用 gWidgets 在 R 中制作 GUI。到目前为止,我一直在通过全局环境将值从一个窗口传递到另一个窗口。使用全局环境实现起来很简单,但并不理想。一个问题是R CMD check 抱怨缺少对全局变量的可见绑定。

作为该问题的解决方案,引用类已被多个 R 程序员提及。但是要了解引用类在这种情况下是如何工作的,举一个简单的例子会很有帮助。

让我提供一个愚蠢的 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)

如何在本例中使用引用类?

【问题讨论】:

    标签: r user-interface global-variables gwidgets reference-class


    【解决方案1】:

    调用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))
    }
    

    【讨论】:

    • 很好的例子。代码给出警告: In .checkFieldsInMethod(def, fieldNames, allMethods) : 对字段名称的本地分配不会改变字段:modelData
    • @JacobVanEtten 谢谢。我已经修复了modelData &lt;- 行。 visible &lt;- 行应该是本地分配。如果您不满意,请将对 setRefClass 的调用封装在 suppressWarnings 中。
    • 谢谢!尽管我会先听从 John 的建议,但在网上有这个例子真是太好了。
    • 我现在检查得更彻底了。模型未正确更新。当您点击 Make model 按钮时,它将生成一个模型,但 Make prediction 将坚持第一个模型。第二次点击“制作模型”按钮不会产生不同的结果,但应该会。
    • @JacobVanEtten:如果你想每次使用不同的数据,那么将modelData &lt;&lt;- data.frame(x = runif(10), y = runif(10))移动到makeModel函数中。
    【解决方案2】:

    Richie 的回答是一种方法。它为您提供了一个对象(generate_silly_gui 返回的实例,您可以使用它来操作模型用于 GUI 的小部件。一个好方法。以下更简单,它只是模型,只是与问题中的代码略有偏差。这里使用引用类是更结构化的使用环境的方式:

    OurModel <- setRefClass("OurModel",
                           fields="m")
    
    ## a global
    model_instance = OurModel$new(m=NULL)
    

    然后只需在您的代码中将.GlobalEnv$m 替换为model_instance$m 并运行。

    使用引用类可以让你做一些事情,比如添加 getter 和 setter,它们也可以做其他事情,并将你推向 model-view-controller 风格。 objectSignals 包朝着这个方向发展。

    如果您的 GUI 变得更复杂,您可能希望将这两种方法分离。

    【讨论】:

    • 很好的例子,我会先试试这个,然后看看objectSignalsR CMD CHECK 使用这种方法时不会抱怨可见绑定?
    • 我只是做了一个小测试。我在包的 .onLoad 函数中创建了一个引用类的实例,然后在包的函数中使用它。这仍然会产生全局绑定问题......当然有一些肮脏的技巧可以避免这种情况:link。但我认为引用类会使这变得不必要......
    • 可以在代码中创建实例。参见例如github.com/jverzani/gWidgets2RGtk2/blob/master/R/icons.R#L106
    • 是的,这将取消该功能的注释。但是当在另一个函数中调用它时,我会抛出一个 NOTE...
    猜你喜欢
    • 2011-08-02
    • 1970-01-01
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多