通过循环分配处理程序的问题

时间:2014-09-07 08:40:32

标签: r loops handler lazy-evaluation gwidgets

How to add a context menu to a `gframe`?的背面,我正在尝试使用不同的上下文菜单处理程序创建三个不同的gframe对象。为避免代码重复,我通过循环执行此操作。

考虑这个最小的例子:

require(gWidgets2)
require(gWidgets2RGtk2)
w <- gwindow()
gg <- gvbox(cont=w)                   

f_lyt_ctab <- list()
l_lyt_ctab <- list()

h_ctab_clear  <- function(field.nr=NULL){
    stopifnot(!is.null(field.nr))
    print(field.nr)
}

lyt_ctab <- glayout(homogeneous=F, cont=gg, expand=TRUE, fill=T)
field.nms <- c("Row Fields", "Column Fields", "Values")
for(i in 1:3){
    lyt_ctab[1,i, expand=TRUE, fill=T] <- 
        f_lyt_ctab[[i]] <- gframe("", horizontal=FALSE,
                                  container=lyt_ctab, expand=TRUE, fill=T)
    ##have gframe with custom label (and context menu)
    l_lyt_ctab[[i]] <- glabel(field.nms[i])
    tooltip(l_lyt_ctab[[i]]) <- paste(
        "Right-click on", field.nms[i], "to clear field variables")
    print(i)
    print(field.nms[i])
    add3rdmousePopupMenu(l_lyt_ctab[[i]], 
                         list(a=gaction("Clear field", icon="clear", 
                                        handler=function(h, ...){
                                            h_ctab_clear(field.nr=i)
                                        })))
    f_lyt_ctab[[i]]$block$setLabelWidget(l_lyt_ctab[[i]]$block)         # the voodoo
    l_lyt_ctab[[i]]$widget$setSelectable(FALSE)           # may not be needed
}

麻烦是由于某种原因

handler=function(h, ...){ h_ctab_clear(field.nr=i) })

似乎没有传递正确的i值。它总是3。因此,无论访问哪个上下文菜单,它都只是执行的h_ctab_clear(field.nr=3)

如果由于gframe工具提示都正确的简单原因,我很难过。但不是与每个上下文菜单相关联的处理程序。

我怀疑h_ctab_clear(field.nr=i)有一些范围或类似问题,但我不确定是什么问题?

1 个答案:

答案 0 :(得分:1)

我猜想add3rdmousePopupMenu可能正在使用handler函数并在不同的上下文中对其进行评估。此时,它无法解析i,因为它是函数体中的自由变量,并且原始机箱环境不再可用。一种可能的解决方案是明确创建一个包含i值的机箱。为了使这更容易,我们可以创建一个辅助函数。

makehandler <- function(i) {
    force(i); 
    function(h, ...){
        h_ctab_clear(field.nr=i)
    }
}

此函数将包含i,然后返回一个可用作处理程序的函数。然后你可以像

一样使用它
add3rdmousePopupMenu(l_lyt_ctab[[i]], 
    list(a=gaction(
        "Clear field", 
        icon="clear", 
        handler=makehandler(i)
    ))
)