Как вернуть значения из gWidgets и обработчиков?

Я пытаюсь разработать графический интерфейс (используя gWidgets) для пакета R. Мой план состоял в том, чтобы создать главное окно, содержащее данные, и с кнопками, вызывающими небольшие графические оболочки для каждой функции. К сожалению, я застрял на основной(?) проблеме - я не знаю, как передать данные.

Вопросы:

  • Как правильно отправлять данные между отдельными окнами?
  • Как отправить данные из обработчика в другое окно?

Моя проблема похожа на: Загрузка и сохранение переменных в R с помощью gWidgets, но из того, что я читал, использование .GlobalEnv не рекомендуется.

Я также видел, как кто-то использовал оператор ‹‹-: 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

}

person Oskar Hansson    schedule 16.03.2013    source источник
comment
Проще всего использовать глобальные переменные для виджетов, которые вы хотите изменить. В качестве альтернативы вы можете поместить переменные в среду. Поскольку они определены, они находятся в среде двух функций, поэтому они не могут взаимодействовать.   -  person jverzani    schedule 17.03.2013
comment
Спасибо за ваше предложение! Как мне использовать глобальные переменные таким образом, чтобы не было риска перезаписи каких-либо существующих объектов в рабочей области (и это приемлемо для пакета, отправленного в CRAN)? Я был бы признателен, если бы кто-нибудь мог показать правильный путь, изменив мой пример выше.   -  person Oskar Hansson    schedule 17.03.2013


Ответы (3)


Лучший подход, но требующий более серьезной переработки вашего кода, состоит в том, чтобы хранить графические интерфейсы в ссылочных классах.

Вы вызываете 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(...))
}    
person Richie Cotton    schedule 17.03.2013
comment
Это звучит как хорошее решение. Однако, когда я пробую код, я получаю сообщение об ошибке в строке перед createMainGui: Ошибка в makeUsageCollector(fun,...): работает только для закрытия. Любая идея, что вызывает это? - person Oskar Hansson; 17.03.2013
comment
К сожалению, я использовал getCurrentText <- function() вместо getCurrentText = function(). Теперь исправлено. - person Richie Cotton; 18.03.2013
comment
Спасибо, после добавления { после setText=function(newTxt) пример работает. Я немного поиграю с этим и посмотрю, как он себя чувствует. - person Oskar Hansson; 18.03.2013
comment
Я думаю, что это «правильный» способ сделать это, поэтому я приму это как ответ. Однако, поскольку у меня уже было довольно много кода, я решил его по-другому с меньшими изменениями (как предложил jverzani). См. новый пост. - person Oskar Hansson; 18.03.2013

Вы можете просто вернуть виджеты для каждого окна в списке. Таким образом, основная функция добавляет строку 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()
person Richie Cotton    schedule 17.03.2013

Вы можете передавать информацию между виджетами с помощью независимых функций и без предварительного знания имени объекта виджета-получателя:

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)
person Egus    schedule 22.08.2013