Использование rhandsontable в блестящем модуле - PullRequest
0 голосов
/ 05 июня 2019

Приложение

При запуске генерируется таблица 3 x 3 со значениями от 1 до 9 в случайном порядке.Пользователь приложения может видеть пустые 3 x 3 rhandsontable, которые он / она будет использовать, чтобы попытаться угадать, где находятся сгенерированные значения.Когда пользователь нажимает кнопку «Отправить», ячейки с правильными значениями становятся зелеными, а все остальные ячейки остаются такими, как есть.

Моя проблема

Ячейки, в которых пользователь угадалне зеленеть, когда пользователь нажимает кнопку.Другими словами, условное форматирование не работает, хотя я и раньше заставлял его работать (это было в первой версии приложения, когда я не использовал блестящие модули).

Что я сделал

Полный проект находится в следующем репозитории Github, который потенциальные пользователи могут захотеть клонировать вместо копирования и вставки приведенного ниже кода: https://github.com/gueyenono/number_game

В папке моего проекта 4 файла.Первые два файла - это обычные ui.R и server.R, которые по сути называют блестящими модулями (то есть hot_module_ui() и hot_module()).Модули содержатся в файле global.R.Последний файл, update_hot.R, содержит функцию, используемую в модулях.

ui.R

Этот файл загружает необходимые пакеты, предоставляет название для приложения и вызывает hot_module_ui().Модуль просто отображает пустой 3 x 3 rhandsontable и actionButton().

library(shiny)
library(rhandsontable)
source("R/update_hot.R")

ui <- fluidPage(

  titlePanel("The number game"),

  mainPanel(
    hot_module_ui("table1")
  )
)

server.R

Этот файл вызывает hot_module(), который содержит код дляусловное форматирование.

server <- function(input, output, session) {
  callModule(module = hot_module, id = "table1")
}

update_hot.R

Эта функция вызывается при вызове кнопки «Отправить».Функция имеет два аргумента:

  • hot: handsontable в приложении
  • x: значения, сгенерированные при запуске

Эточто делает функция (полный код файла находится в конце этого раздела):

  1. Получить пользовательские данные
user_input <- hot_to_r(hot)
Сравните пользовательские данные (user_input) с истинными значениями (x) и сохраните индексы строк и столбцов ячеек, в которых пользователь угадал право
i <- which(user_input == x, arr.ind = TRUE)

  row_correct <- i[, 1] - 1
  col_correct <- i[, 2] - 1
Обновите текущий объект handsftable с помощью индексов строк и столбцов и используйте аргумент renderer функции hot_cols(), чтобы сделать фон соответствующих ячеек зеленым.Обратите внимание, что я использую функцию hot_table() для обновления существующего объекта rhandsontable.
hot %>%
    hot_table(contextMenu = FALSE, row_correct = row_correct, col_correct = col_correct) %>%
    hot_cols(renderer = "function(instance, td, row, col, prop, value, cellProperties){
          Handsontable.renderers.TextRenderer.apply(this, arguments);

          if(instance.params){

            // Correct cell values
            row_correct = instance.params.row_correct
            row_correct = row_correct instanceof Array ? row_correct : [row_correct]
            col_correct = instance.params.col_correct
            col_correct = col_correct instanceof Array ? col_correct : [col_correct]


            for(i = 0; i < col_correct.length; i++){ 
              if (col_correct[i] == col && row_correct[i] == row) {
                  td.style.background = 'green';
              } 
            }

          return td;
        }")

Вот полный код для update_hot.R

update_hot <- function(hot, x){

  # Get user inputs (when the submit button is clicked)
  user_input <- hot_to_r(hot)

  # Get indices of correct user inputs
  i <- which(user_input == x, arr.ind = TRUE)

  row_correct <- i[, 1] - 1
  col_correct <- i[, 2] - 1

  # Update the hot object with row_index and col_index for user in the renderer
  hot %>%
    hot_table(contextMenu = FALSE, row_correct = row_correct, col_correct = col_correct) %>%
    hot_cols(renderer = "function(instance, td, row, col, prop, value, cellProperties){
          Handsontable.renderers.TextRenderer.apply(this, arguments);

          if(instance.params){

            // Correct cell values
            row_correct = instance.params.row_correct
            row_correct = row_correct instanceof Array ? row_correct : [row_correct]
            col_correct = instance.params.col_correct
            col_correct = col_correct instanceof Array ? col_correct : [col_correct]


            for(i = 0; i < col_correct.length; i++){ 
              if (col_correct[i] == col && row_correct[i] == row) {
                  td.style.background = 'green';
              } 
            }

          return td;
        }")
}

global.R

Это файл, который содержит блестящие модули.Модуль пользовательского интерфейса (hot_module_ui()) имеет: - rHandsontableOutput - actionButton - я добавил tableOutput, чтобы увидеть, где находятся сгенерированные значения (полезно для тестирования кода)

серверный модуль (hot_module()) вызывает функцию update_hot() и пытается обновить handsontable в приложении всякий раз, когда пользователь нажимает кнопку «Отправить».Я попытался достичь этого, используя observeEvent и реактивное значение react$hot_display.При запуске react$hot_display содержит фрейм данных 3 x 3 NA с.Когда кнопка нажата, она обновляется новой версией handsontable (содержащей пользовательские данные и условное форматирование).Вот полный код для global.R:

hot_module_ui <- function(id){

  ns <- NS(id)

  tagList(
    rHandsontableOutput(outputId = ns("grid")),
    br(),
    actionButton(inputId = ns("submit"), label = "Submit"),
    br(),
    tableOutput(outputId = ns("df"))
  )

}


hot_module <- function(input, output, session){

  values <- as.data.frame(matrix(sample(9), nrow = 3))

  react <- reactiveValues()

  observe({
    na_df <- values
    na_df[] <- as.integer(NA)
    react$hot_display <-  rhandsontable(na_df, rowHeaders = NULL, colHeaders = NULL)
  })

  observeEvent(input$submit, {
    react$hot_display <- update_hot(hot = input$grid, x = values)
  })

  output$grid <- renderRHandsontable({
    react$hot_display
  })

  output$df <- renderTable({
    values
  })
}

Как уже упоминалось в начале, условное форматирование не работает при нажатии кнопки «Отправить», и я не уверен, почему.Еще раз, вы можете получить доступ к полному коду в следующем репозитории Github:

https://github.com/gueyenono/number_game

1 Ответ

0 голосов
/ 12 июня 2019

Я наконец нашел решение своей проблемы.Один из самых больших уроков, которые я выучил, заключался в том, что функция hot_to_r() не работает в пользовательских функциях.Он должен использоваться в функции сервера блестящего приложения.Это означает, что передача объекта rhandsontable в пользовательскую функцию и извлечение данных из этой функции может быть не очень хорошей идеей (это было моей историей).

Я не уверен, что это будет интереснокто угодно, но вот мой код, который работает как задумано:

ui.R

library(rhandsontable)
library(shiny)
source("R/update_hot.R")

shinyUI(fluidPage(

    # Application title
    titlePanel("The Number Game"),

    module_ui(id = "tab")
))

server.R

library(shiny)

shinyServer(function(input, output, session) {

    callModule(module = module_server, id = "tab")

})

global.R

module_ui <- function(id){

  ns <- NS(id)

  tagList(
    rHandsontableOutput(outputId = ns("hot")),
    actionButton(inputId = ns("submit"), label = "OK"),
    actionButton(inputId = ns("reset"), label = "Reset")
  )
}


module_server <- function(input, output, session){

  clicked <- reactiveValues(submit = FALSE, reset = FALSE)

  initial_hot <- rhandsontable(as.data.frame(matrix(NA_integer_, nrow = 3, ncol = 3)))
  correct_values <- as.data.frame(matrix(1:9, nrow = 3, byrow = TRUE))

  observeEvent(input$submit, {
    clicked$submit <- TRUE
    clicked$reset <- FALSE
  })

  updated_hot <- eventReactive(input$submit, {
    input_values <- hot_to_r(input$hot)
    update_hot(input_values = input_values, correct_values = correct_values)
  })


  observeEvent(input$reset, {
    clicked$reset <- TRUE
    clicked$submit <- FALSE
  })

  reset_hot <- eventReactive(input$reset, {
    initial_hot
  })


  output$hot <- renderRHandsontable({

    if(!clicked$submit & !clicked$reset){
      out <- initial_hot
    } else if(clicked$submit & !clicked$reset){
      out <- updated_hot()
    } else if(clicked$reset & !clicked$submit){
      out <- reset_hot()
    }

    out
  })
}

R / update_hot.R

update_hot <- function(input_values, correct_values){

  equal_ids <- which(input_values == correct_values, arr.ind = TRUE)
  unequal_ids <- which(input_values != correct_values, arr.ind = TRUE)

  rhandsontable(input_values) %>%
    hot_table(row_correct = as.vector(equal_ids[, 1]) - 1,
              col_correct = as.vector(equal_ids[, 2]) - 1,
              row_incorrect = as.vector(unequal_ids[, 1]) - 1,
              col_incorrect = as.vector(unequal_ids[, 2]) - 1) %>%

    hot_cols(renderer = "function(instance, td, row, col, prop, value, cellProperties){
          Handsontable.renderers.TextRenderer.apply(this, arguments);

          if(instance.params){

            // Correct cell values
            row_correct = instance.params.row_correct
            row_correct = row_correct instanceof Array ? row_correct : [row_correct]
            col_correct = instance.params.col_correct
            col_correct = col_correct instanceof Array ? col_correct : [col_correct]

            // Incorrect cell values
            row_incorrect = instance.params.row_incorrect
            row_incorrect = row_incorrect instanceof Array ? row_incorrect : [row_incorrect]
            col_incorrect = instance.params.col_incorrect
            col_incorrect = col_incorrect instanceof Array ? col_incorrect : [col_incorrect]


            for(i = 0; i < col_correct.length; i++){ 
              if (col_correct[i] == col && row_correct[i] == row) {
                  td.style.background = 'green';
              } 
            }

            for(i = 0; i < col_incorrect.length; i++){ 
              if (col_incorrect[i] == col && row_incorrect[i] == row) {
                  td.style.background = 'red';
              } 
            }
          }
          return td;
        }")
}
...