Как вернуть значения из gWidgets и обработчиков?
Я пытаюсь разработать GUI (используя gWidgets) для пакета R. Мой план состоял в том, чтобы построить главное окно с данными и кнопками, вызывающими небольшие оболочки gui для каждой функции. К сожалению, я застрял на основной(?) проблема-я не знаю, как передавать данные.
любыми вопросами:
- Как правильно передавать данные между отдельными окнами?
- как отправить данные из обработчика в другом окне?
мой проблема аналогична: загрузка и сохранение переменных в R с помощью 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.
чтобы подвести итог, я создал новую среду в среде главного окна. Я отредактировал окно sub, чтобы взять среду в вызове. Нажатие кнопки в подокне 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
}
3 ответов
лучший подход, но тот, который включает большую переработку вашего кода, заключается в хранении GUIs в ссылочных классах.
вы называете setRefClass
со списком полей (по одному для каждого виджета) и определите метод инициализации, в котором создается графический интерфейс. Обычно я создаю функцию для переноса вызова, который создает экземпляр. См.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(...))
}
вы можете просто вернуть виджеты для каждого окна в списке. Таким образом, основная функция добавляет строку 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()
вы можете передавать информацию между 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)