8

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

问题:

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

我的问题类似于: Loading and save variables in R with gWidgets,但从我读过的内容来看,不推荐使用 .GlobalEnv 。

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

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

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 建议的那样),使用上面的示例进行说明。我希望我正确理解了建议的解决方案,并且我已经以一种“不错”的方式实施了它,在 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

}
4

3 回答 3

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(...))
}    
于 2013-03-17T09:41:13.087 回答
2

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

以下示例是对您的代码进行的最小更改,但其中有一个小缺陷,main现在subwindow包含对彼此返回值的引用。该代码有效,但如果您正在做一些更复杂的事情,它很容易变得难以维护。

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()
于 2013-03-17T09:23:10.747 回答
1

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

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)
于 2013-08-22T16:30:04.740 回答