2013-03-16 49 views
8

我想爲R包開發一個GUI(使用gWidgets)。我的計劃是構建一個包含數據的主窗口,並使用按鈕調用每個函數的小型gui包裝。不幸的是我被困在一個基本的(?)問題 - 我不知道如何傳輸數據。如何從gWidgets和處理程序返回值?

Questons:

  • 如何正確單獨的窗口之間發送數據?
  • 如何從另一個窗口中的處理程序發送數據?

我的問題是相似的: Loading and saving variables in R with gWidgets,但是從我已閱讀使用.GlobalEnv不推薦。

我也看到有人使用< < - operator:http://www.mail-archive.com/[email protected]/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所建議的),使用上面的例子中,示出的解決方案。我希望我能夠正確理解所提出的解決方案,並且我已經以「很好」的方式實現了它,並在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 

} 
+1

最簡單的事情是使用全局變量要小部件修改。或者,您可以將變量放入環境中。正如他們所定義的,他們坐在兩個功能的環境中,所以兩者不能溝通。 – jverzani 2013-03-17 02:16:55

+0

感謝您的建議!我如何以不會冒險覆蓋工作區中任何現有對象的方式使用全局變量(並且被提交給CRAN的包所接受)?我將不勝感激,如果有人可以通過修改上面的示例顯示正確的方式。 – 2013-03-17 07:44:50

回答

5

一種更好的方法,但涉及到代碼更大更新的方法是將GUI存儲在引用類中。

你打電話setRefClass與(一個用於每個插件)字段的列表,以及定義在其中創建GUI的方法的initialise。我通常創建一個函數來包裝創建實例的調用。請參閱代碼塊末尾的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(...)) 
}  
+0

這聽起來像是一個不錯的解決方案。但是,當我嘗試代碼時,我在'createMainGui'之前得到了一行錯誤:makeUsageCollector(fun,...)中的錯誤:僅適用於閉包。任何想法是什麼導致它? – 2013-03-17 15:55:53

+0

糟糕,我使用'getCurrentText < - function()'而不是'getCurrentText = function()'。現在修復。 – 2013-03-18 08:39:40

+0

謝謝,在'setText = function(newTxt)'之後添加了一個'{'後,該示例正在運行。我會玩這個,看看它的感覺。 – 2013-03-18 09:30:04

1

您可以直接返回列表中每個窗口的小部件。因此,主函數在末尾添加行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() 
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)