基本问题似乎是我无法从内部函数中检索小部件的状态。
我正在尝试使使用与gWidgets2tcltk兼容的gWidgets2RGtk2开发的程序包。该软件包包括一个图形用户界面,实现了保存gui状态的可能性。该代码按使用RGtk2的预期工作,但是使用tcltk存在问题。我尝试了if构造的各种变体。我感到奇怪的是,我可以从.loadState内部读取状态,而不能从.saveState内部读取状态。他们是从不同的地方打电话来的,那么问题可能与环境有关吗?我被困了一段时间,所以也许我对一个显而易见的解决方案视而不见,或者也许有一种更好的方法来完成我要执行的行为。下面的代码说明了这个问题。
我正在Windows 10系统上使用R版本3.5.1,gWidgets2tcltk_1.0-6和gWidgets2_1.0-7。
# The code works as intended using RGtk2.
require(gWidgets2RGtk2)
options("guiToolkit"="RGtk2")
# The code does not work when using tcltk.
# require(gWidgets2tcltk)
# options("guiToolkit" = "tcltk")
saveStateExample <- function(env = parent.frame(), savegui = NULL) {
# savegui = NULL, Default when started manually as GUI wrapped function.
# savegui = TRUE, Passed from main GUI when started as part of full program.
# savegui = FALSE, Passed from main GUI when started as part of full program.
# Create windows.
w <- gwindow(title = "Checkbox behaviour", visible = FALSE)
# Runs when window is closed.
addHandlerDestroy(w, handler = function(h, ...) {
# Save GUI state.
.saveState()
})
# Create container.
g <- ggroup(container = w, expand = TRUE, horizontal = FALSE)
# Add checkbox to control saving gui statesa.
save_state_chk <- gcheckbox(text = "Save state", checked = FALSE, container = g)
# Add a text widget.
text_edt <- gedit(container = g)
# Add buttons to manually trigger the functions.
load_btn <- gbutton(text = "Run .loadState", container = g)
save_btn <- gbutton(text = "Run .saveState", container = g)
addHandlerChanged(load_btn, handler = function(h, ...) {
.loadState()
})
addHandlerChanged(save_btn, handler = function(h, ...) {
.saveState()
})
# Internal function ---------------------------------------------------------
.loadState <- function() {
message(".loadState")
message("save_state_chk was ", svalue(save_state_chk))
message("savegui was ", savegui)
# First check if save argument was passed.
if (!is.null(savegui)) {
# Update widget with passed value.
svalue(save_state_chk) <- savegui
message("save_state_chk set to ", savegui)
message("save_state_chk is ", svalue(save_state_chk))
} else {
# Look for previously saved flag.
if (exists(".package_savegui", envir = env, inherits = FALSE)) {
svalue(save_state_chk) <- get(".package_savegui", envir = env)
message(".package_savegui loaded")
}
}
message("LOAD SAVED STATE")
# Then load settings if true.
if (svalue(save_state_chk)) {
if (exists(".package_text", envir = env, inherits = FALSE)) {
svalue(text_edt) <- get(".package_text", envir = env)
}
message("GUI saved state loaded")
} else {
message("GUI default state loaded")
}
}
# Internal function ---------------------------------------------------------
.saveState <- function() {
message(".saveState")
message("save_state_chk was ", svalue(save_state_chk))
message("savegui was ", savegui)
# First check status of save flag.
if (is.null(svalue(save_state_chk))) {
message("save_state_chk=NULL")
} else {
message("SAVE STATE")
# Then save settings if true.
if (svalue(save_state_chk)) {
assign(x = ".package_savegui", value = svalue(save_state_chk), envir = env)
assign(x = ".package_text", value = svalue(text_edt), envir = env)
message("GUI state saved")
} else { # or remove all saved values if false.
if (exists(".package_savegui", envir = env, inherits = FALSE)) {
remove(".package_savegui", envir = env)
}
if (exists(".package_text", envir = env, inherits = FALSE)) {
remove(".package_text", envir = env)
}
message("GUI state cleared")
}
}
}
# Run internal function to load state before showing window.
.loadState()
visible(w) <- TRUE
}
# Open gui.
saveStateExample()
EDIT1: 我在下面做了一个最小化的示例,该示例可用于您描述的两种工具包。目的是确保内部功能按预期方式工作。
为了进一步查找问题,我编辑了上面的第一个代码示例,其中包括手动触发内部功能的按钮。按钮起作用,状态可以读取,并且可以通过message
功能进行打印。但是,当销毁处理程序被触发时(使用tcltk工具包),该状态仍未读取。似乎使用tcltk工具包破坏了内容为时过早,但没有使用RGtk2工具包破坏了内容。有什么想法吗?
# The code works as intended using RGtk2 and tcltk.
# require(gWidgets2RGtk2)
# options("guiToolkit"="RGtk2")
require(gWidgets2tcltk)
options("guiToolkit" = "tcltk")
saveStateExample <- function() {
# Create windows.
w <- gwindow(title = "Checkbox behaviour", visible = FALSE)
# Runs when window is closed.
addHandlerDestroy(w, handler = function(h, ...) {
message("Window destroyed")
message("save_state_chk is ", svalue(save_state_chk))
message("text_edt is ", svalue(text_edt))
})
# Create container.
g <- ggroup(container = w, expand = TRUE, horizontal = FALSE)
# Add checkbox to control saving gui statesa.
save_state_chk <- gcheckbox(text = "Save state", checked = FALSE, container = g)
# Add a text widget.
text_edt <- gedit(container = g)
# Add buttons.
check_btn <- gbutton(text = "Check", container = g)
uncheck_btn <- gbutton(text = "UnCheck", container = g)
addHandlerChanged(check_btn, handler = function(h, ...) {
.setCheckTrue()
})
addHandlerChanged(uncheck_btn, handler = function(h, ...) {
.setCheckFalse()
})
# Internal function ---------------------------------------------------------
.setCheckTrue <- function() {
message(".setCheckTrue")
message("save_state_chk was ", svalue(save_state_chk))
svalue(save_state_chk) <- TRUE
message("save_state_chk is ", svalue(save_state_chk))
message("text_edt is ", svalue(text_edt))
}
# Internal function ---------------------------------------------------------
.setCheckFalse <- function() {
message(".setCheckFalse")
message("save_state_chk was ", svalue(save_state_chk))
svalue(save_state_chk) <- FALSE
message("save_state_chk is ", svalue(save_state_chk))
message("text_edt is ", svalue(text_edt))
}
# Show window.
visible(w) <- TRUE
}
# Open gui.
saveStateExample()
EDIT2:
感谢您提供@jverzani的提示,我已经按照您的建议尝试了addHandlerUnrealize。这就解决了不读取tcltk的任何值的问题。但是,我花了一些时间来整理内容并使其与两个工具箱一起使用-无法关闭窗口。我在文档中没有找到太多内容,但是通过反复试验,似乎RGtk2和tcltk实施了不同的物流。要继续进行销毁事件,RGtk2需要FALSE
,而tcltk需要TRUE
。下面是第一个代码示例的固定版本:
# The code now works as intended using both RGtk2 and tcltk!
# require(gWidgets2RGtk2)
# options("guiToolkit"="RGtk2")
require(gWidgets2tcltk)
options("guiToolkit" = "tcltk")
saveStateExample <- function(env = parent.frame(), savegui = NULL) {
# savegui = NULL, Default when started manually as GUI wrapped function.
# savegui = TRUE, Passed from main GUI when started as part of full program.
# savegui = FALSE, Passed from main GUI when started as part of full program.
# Create windows.
w <- gwindow(title = "Checkbox behaviour", visible = FALSE)
# Runs when window is closed.
addHandlerUnrealize(w, handler = function(h, ...) {
# Save GUI state.
.saveState()
message("UNREALIZE!")
# Check which toolkit we are using.
if (gtoolkit() == "tcltk") {
message("tcltk, returned TRUE!")
return(TRUE) # Destroys window under tcltk, but not RGtk2.
} else {
message("RGtk2, returned FALSE!")
return(FALSE) # Destroys window under RGtk2, but not with tcltk.
}
})
# Runs when window is closed.
addHandlerDestroy(w, handler = function(h, ...) {
message("DESTROY!")
# addHandlerDestroy does not care of return type for either RGtk2 or tcltk?
})
# Create container.
g <- ggroup(container = w, expand = TRUE, horizontal = FALSE)
# Add checkbox to control saving gui statesa.
save_state_chk <- gcheckbox(text = "Save state", checked = FALSE, container = g)
# Add a text widget.
text_edt <- gedit(container = g)
# Add buttons to manually trigger the functions.
load_btn <- gbutton(text = "Run .loadState", container = g)
save_btn <- gbutton(text = "Run .saveState", container = g)
addHandlerChanged(load_btn, handler = function(h, ...) {
.loadState()
})
addHandlerChanged(save_btn, handler = function(h, ...) {
.saveState()
})
# Internal function ---------------------------------------------------------
.loadState <- function() {
message(".loadState")
message("save_state_chk was ", svalue(save_state_chk))
message("savegui was ", savegui)
# First check if save argument was passed.
if (!is.null(savegui)) {
# Update widget with passed value.
svalue(save_state_chk) <- savegui
message("save_state_chk set to ", savegui)
message("save_state_chk is ", svalue(save_state_chk))
} else {
# Look for previously saved flag.
if (exists(".package_savegui", envir = env, inherits = FALSE)) {
svalue(save_state_chk) <- get(".package_savegui", envir = env)
message(".package_savegui loaded")
}
}
message("LOAD SAVED STATE")
# Then load settings if true.
if (svalue(save_state_chk)) {
if (exists(".package_text", envir = env, inherits = FALSE)) {
svalue(text_edt) <- get(".package_text", envir = env)
}
message("GUI saved state loaded")
} else {
message("GUI default state loaded")
}
}
# Internal function ---------------------------------------------------------
.saveState <- function() {
message(".saveState")
message("save_state_chk was ", svalue(save_state_chk))
message("savegui was ", savegui)
# First check status of save flag.
if (is.null(svalue(save_state_chk))) {
message("save_state_chk=NULL")
} else {
message("SAVE STATE")
# Then save settings if true.
if (svalue(save_state_chk)) {
assign(x = ".package_savegui", value = svalue(save_state_chk), envir = env)
assign(x = ".package_text", value = svalue(text_edt), envir = env)
message("GUI state saved")
} else { # or remove all saved values if false.
if (exists(".package_savegui", envir = env, inherits = FALSE)) {
remove(".package_savegui", envir = env)
}
if (exists(".package_text", envir = env, inherits = FALSE)) {
remove(".package_text", envir = env)
}
message("GUI state cleared")
}
}
}
# Run internal function to load state before showing window.
.loadState()
visible(w) <- TRUE
}
# Open gui.
saveStateExample()
答案 0 :(得分:0)
问题的原因是addHandlerDestroy
不适合保存小部件状态。它可能像RGtk2一样起作用,但是不能保证此时可以访问小部件。解决方法是改为使用addHandlerUnrealize
,正如@jverzani在评论中指出的那样:
您应该在这里尝试addHandlerUnrealize(尽管我不知道这是否 适用于RGtk2,无法测试)。只要那只要 您的处理程序不会返回FALSE,但会在销毁之前执行 事件,因此您的窗口小部件仍然可以读取。 – jverzani 1月24日下午16:38
有趣的是,示例代码揭示了用不同的方式来实现信号销毁窗口的实现(有关变通方法,请参见原始文章)。请注意,@ jverzani考虑推送修复程序,它将对此进行更改。