如何从gWidgets和处理程序返回值?

时间:2013-03-16 22:55:00

标签: r gwidgets

我正在尝试为R包开发GUI(使用gWidgets)。我的计划是构建一个包含数据的主窗口,并使用按钮为每个函数调用小的gui包装器。不幸的是我遇到了一个基本的(?)问题 - 我不知道如何传输数据。

Questons:

  • 如何在不同的窗口之间正确发送数据?
  • 如何从另一个窗口中的处理程序中发送数据?

我的问题类似于: Loading and saving variables in R with gWidgets,但据我所知,不推荐使用.GlobalEnv。

我还看到有人使用<< - 运算符:http://www.mail-archive.com/r-sig-gui@r-project.org/msg00053.html,但我无法正确地重现它(我认为它不适用于我的例子)。

下面是一个简单的例子,我尝试将文本发送到另一个窗口,如果按下按钮则再次返回。我已尝试在处理程序内部返回,但这不起作用(也不确定是否允许)。子窗口在处理程序/内部函数可以对数据执行操作之前立即在函数末尾返回其值。我不知道如何从处理程序到主窗口。

main <- function(){

  library(gWidgets)
  options(guiToolkit="RGtk2")

  w <- gwindow(title="Main window",
               visible=FALSE)

  txt <- gtext(text="Initial text in main window.",
               container=w)

  btn <- gbutton("Send to sub window", container=w)

  addHandlerChanged(btn, handler = function(h, ...) {
    shouldbenew <- subwindow(svalue(txt))
    svalue(txt) <- paste("The sub window immediately returns 'TRUE', before pushing 'Return to main':", shouldbenew )
  } )

  visible(w) <- TRUE

}

subwindow<- function(text){

  library(gWidgets)
  options(guiToolkit="RGtk2")

  sw <- gwindow(title="Sub window",
                visible=FALSE)

  editedtxt <- gtext(text=paste(text, "- Text is transferred to the sub window, but I don't know how to send it back to the main window"),
                     container=sw)

  btn <- gbutton("Send to main window", container=sw)

  addHandlerChanged(btn, handler = function(h, ...) {
    newtxt <- svalue(editedtxt)
    return(newtxt)

  } )

  visible(sw) <- TRUE

}

更新 这是我选择的解决方案(正如jverzani所建议的那样),使用上面的例子说明。我希望我理解建议的解决方案是正确的,并且我已经在一个很好的&#39;中实现了它。方式,理想情况下在CRAN接受。

总结一下,我在主窗口环境中创建了一个新环境。我编辑了子窗口以获取呼叫中的环境。将子窗口中的按钮assign按下已编辑的文本到传递的环境。当子窗口关闭,主窗口聚焦时,可以使用get从环境访问编辑的文本。

main <- function(){

  library(gWidgets)
  options(guiToolkit="RGtk2")
  # Create a new environment for passing data.
  .mainGlobal <- new.env()

  w <- gwindow(title="Main window", visible=FALSE)

  txt <- gtext(text="Initial text in main window.",
               container=w)

  btn <- gbutton("Send to sub window", container=w)

  addHandlerChanged(btn, handler = function(h, ...) {
    # Call sub widget passing text and environment.
    subwindow(text=svalue(txt), env=.mainGlobal)
  } )

  visible(w) <- TRUE

  addHandlerFocus(w, handler = function (h, ...) {

    if(exists("myText", envir=.mainGlobal)){
      # Retrieve text and update.
      svalue(txt) <- get("myText", envir=.mainGlobal)
    }    
  })

}

subwindow<- function(text, env=NULL){

  library(gWidgets)
  options(guiToolkit="RGtk2")

  sw <- gwindow(title="Sub window", visible=FALSE)

  editedtxt <- gtext(text=text, container=sw)

  btn <- gbutton("Send to main window", container=sw)

  addHandlerChanged(btn, handler = function(h, ...) {
    newtxt <- svalue(editedtxt)
    assign("myText", newtxt, envir=env)
  } )

  visible(sw) <- TRUE

}

3 个答案:

答案 0 :(得分:5)

更好的方法,但涉及更大的代码重写,是将GUI存储在引用类中。

使用字段列表(每个窗口小部件一个)调用setRefClass,并定义创建GUI的初始化方法。我通常创建一个函数来包装创建实例的调用。请参阅代码块末尾的setRefClass

mainGui <- suppressWarnings(setRefClass( #Warnings about local assignment not relevant 
  "mainGui",
  fields = list(
    #widgets
    w   = "ANY",       #"GWindow"
    txt = "ANY",       #"GEdit"
    btn = "ANY"        #"GButton"
  ),
  methods = list(
    initialize = function(windowPosition = c(0, 0))
    {
      "Creates the GUI"

      w <<- gwindow(
        "Main window", 
        visible = FALSE,
        parent  = windowPosition
      )

      txt <<- gedit(
        "Initial text in main window.", 
        container = w
      )      

      btn <<- gbutton(
        "Send to sub window", 
        container = w
      )      

      addHandlerChanged(
        btn, 
        handler = function(h, ...) {
          subWindow$setText(getText())
        } 
      )

      visible(w) <- TRUE      
    },
    #other methods to access GUI functionality go here
    getText = function() 
    {
      svalue(txt)
    },
    setText = function(newTxt) 
    {
      svalue(txt) <- newTxt
    }
  )
))  

createMainGui <- function(...)
{
  invisible(mainGui$new(...))
}    

答案 1 :(得分:2)

您只需返回列表中每个窗口的小部件即可。因此,main函数在末尾添加行list(w = w, txt = txt, btn = btn)以返回每个小部件,并在函数完成后使其可访问。

以下示例是对您的代码起作用的最小变化,但是mainsubwindow现在包含对彼此返回值的引用存在一个小缺陷。代码有效,但如果你做的事情比较复杂,很容易变得难以维护。

library(gWidgets)
options(guiToolkit="tcltk")

main <- function(){
  w <- gwindow(title="Main window",
               visible=FALSE)

  txt <- gtext(text="Initial text in main window.",
               container=w)

  btn <- gbutton("Send to sub window", container=w)

  addHandlerChanged(btn, handler = function(h, ...) {
    svalue(subWindow$txt) <- svalue(mainWindow$txt)
  } )

  visible(w) <- TRUE
  list(w = w, txt = txt, btn = btn)
}

subwindow<- function(text){
  sw <- gwindow(title="Sub window",
                visible=FALSE)

  editedtxt <- gtext(text="",
                     container=sw)

  btn <- gbutton("Send to main window", container=sw)

  addHandlerChanged(btn, handler = function(h, ...) {
    svalue(mainWindow$txt) <- svalue(subWindow$txt)
  } )

  visible(sw) <- TRUE
  list(w = sw, txt = editedtxt, btn = btn)
}

mainWindow <- main()
subWindow <- subwindow()

答案 2 :(得分:1)

您可以使用独立函数在gwidgets之间传递信息,而无需事先知道接收器小部件的对象名称:

initMain <- function() {
  w <- gwindow(title="Main window",visible=FALSE)
  txt <- gtext(text="Initial text in main window.",container=w)
  btn <- gbutton("Send to sub window", container=w)

  list(
    run = function(partner) {
      addHandlerChanged(btn, handler = function(h, ...) {
        svalue(partner$txt) <- svalue(txt)
      } )
      visible(w) <- TRUE
    },
    txt = txt
  )
}

initSubWindow<- function() {
  w <- gwindow(title="Sub window",visible=FALSE)
  txt <- gtext(text="huhu",container=w)
  btn <- gbutton("Send to main window", container=w)

  list(
    run = function(partner) {
      addHandlerChanged(btn, handler = function(h, ...) {
        svalue(partner$txt) <- svalue(txt)
      } )
      visible(w) <- TRUE
    },
    txt = txt
  )
}

mw <- initMain()
sw <- initSubWindow()

mw$run(sw)
sw$run(mw)