Как получить состояние флажка в gWidgets2tcltk (работает в gWidgets2RGtk2) - PullRequest
0 голосов
/ 21 января 2019

Основная проблема в том, что я не могу получить состояние виджета из внутренней функции.

Я пытаюсь сделать мой пакет, разработанный с использованием 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()

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()

1 Ответ

0 голосов
/ 29 января 2019

Причиной проблемы было то, что addHandlerDestroy не подходит для сохранения состояния виджета. Это может работать, как и для RGtk2, но нет никакой гарантии, что виджеты будут доступны на этом этапе. Решение состоит в том, чтобы использовать addHandlerUnrealize вместо этого, как отметил @jverzani в комментарии:

Вы должны попробовать addHandlerUnrealize здесь (хотя я не знаю, если это работает для RGtk2 и не может проверить). Это будет призыв уничтожить до тех пор, пока ваш обработчик не возвращает FALSE, но будет выполняться до уничтожения событие, так что ваши виджеты будут по-прежнему доступны для чтения. - джверзани 24 января в 16: 38

Интересно, что пример кода выявил различные реализации сигнализации о разрушении окна (обойти это можно в оригинальном посте). Обратите внимание, что @jverzani рассмотрит возможность исправления, которое изменит это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...