Как получить состояние флажка в gWidgets2tcltk (работает в gWidgets2RGtk2)
Основная проблема заключается в том, что я не могу получить состояние виджета из внутренней функции.
Я пытаюсь сделать мой пакет, разработанный с использованием gWidgets2RGtk2, совместимым с gWidgets2tcltk. Пакет включает в себя графический интерфейс пользователя, реализующий возможность сохранения состояния графического интерфейса. Код работает как задумано с использованием RGtk2, но есть проблемы с использованием tcltk. Я пробовал различные варианты конструкций if. Мне кажется странным, что я могу читать состояние из.loadState, но не из.saveState. Они вызываются из разных мест, так может ли проблема быть связана с окружающей средой? Я застрял на некоторое время, поэтому, может быть, я слеп к очевидному решению, или, возможно, есть гораздо лучший способ добиться того поведения, которого я добиваюсь. Приведенный ниже код иллюстрирует проблему.
Я использую R версии 3.5.1 в системе Windows 10, 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()
РЕДАКТИРОВАТЬ1: я сделал чуть более минимальный пример ниже, который работает с обоими наборами инструментов, как вы описываете. Цель состояла в том, чтобы убедиться, что внутренние функции работали так, как предполагалось, что они и сделали.
Чтобы еще больше отследить проблему, я отредактировал первый пример кода выше, чтобы включить кнопки для ручного запуска внутренних функций. Кнопки работают, состояние может быть прочитано и напечатано 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()
1 ответ
Причиной проблемы было то, что addHandlerDestroy
не подходит для сохранения состояния виджета. Это может работать, как и для RGtk2, но нет никакой гарантии, что виджеты будут доступны на этом этапе. Решение заключается в использовании addHandlerUnrealize
вместо этого, как указал @jverzani в комментарии:
Вы должны попробовать addHandlerUnrealize здесь (хотя я не знаю, работает ли это для RGtk2 и не может тестировать). Это будет вызывать уничтожение до тех пор, пока ваш обработчик не вернет FALSE, но будет выполняться до события уничтожения, поэтому ваши виджеты будут по-прежнему доступны для чтения. - 24 января в 16:38
Интересно, что пример кода показал различные реализации сигнализации о разрушении окна (обойти это можно в оригинальном посте). Обратите внимание, что @jverzani рассмотрит возможность исправления, которое изменит это.