将Shiny应用中的所有用户输入导出到文件并稍后加载

时间:2015-09-08 14:31:28

标签: r shiny

My Shiny应用程序有几个输入,用于定义生成的绘图的几个参数。用户很可能会花费几分钟时间浏览所有可能的选项,直到他对输出感到满意为止。显然,可以以不同的格式导出绘图,但是用户可能希望稍后使用不同的数据重新创建相同的绘图,或者只是更改一个小细节。

因此,我需要为用户提供一种导出所有设置的方法,并保留该文件供以后使用。我已经开发了一种方法,但它运作良好。我使用reactiveValuesToList获取所有输入元素的名称,并另存为格式为inputname=inputvalue的简单文本文件。这是downloadHandler上的server.R

output$bt_export <- downloadHandler(
  filename = function() {
    "export.txt"
  },
  content = function(file) {
    inputsList <- names(reactiveValuesToList(input))
    exportVars <- paste0(inputsList, "=", sapply(inputsList, function(inpt) input[[inpt]]))
    write(exportVars, file)
  })

这很好用,但加载并不顺利。由于我没有(并且无法弄清楚如何)保存输入类型,我必须盲目更新这些值。我就是这样做的:

importFile <- reactive({      
  inFile <- input$fileImport      
  if (is.null(inFile))
    return(NULL)      
  lines <- readLines(inFile$datapath)
  out <- lapply(lines, function(l) unlist(strsplit(l, "=")))      
  return(out)
})

observe({
    imp <- importFile()            
    for (inpt in imp) {
      if (substr(inpt[2], 0, 1) == "#") {
        shinyjs::updateColourInput(session, inputId = inpt[1], value = inpt[2])
      } else {
        try({
          updateTextInput(session, inputId = inpt[1], value = inpt[2])
          updateNumericInput(session, inputId = inpt[1], value = inpt[2])
          updateSelectInput(session, inputId = inpt[1], selected = inpt[2])              
        })
      }
    }       
  })

除了shinyjs::colorInput开头可以识别的#之外,我必须将try()用于其他人。这部分工作,但有些输入没有更新。手动检查导出的文件会显示没有更新的输入,所以我认为一次更新100多个输入并不是一个好主意。此外try()部分看起来并不好看,也许不是一个好主意。

该应用即将完成,但未来可能会更新,添加/更改了一些输入。如果这甚至可以使一些老的&#34;导出的输入无效,因为我会尝试保持向后兼容性。但是,我正在寻找一种不仅仅是编写数百行来逐步更新输入的方法。

我已考虑过使用save.image(),但仅使用load()无法恢复应用输入。我还考虑过以某种方式同时更新所有输入,而不是逐个更新,但没有提出任何建议。有没有更好的方法将所有用户输入导出到文件然后加载它们?如果它对这个更好或完全不同的方法进行调整并不重要。

3 个答案:

答案 0 :(得分:9)

如果查看闪亮输入更新函数的代码,它们将以session$sendInputMessage(inputId, message)结尾。 message是需要在输入中更改的属性列表,例如,对于复选框输入:message <- dropNulls(list(label = label, value = value))

由于大多数输入都具有value属性,因此您可以直接在所有输入上使用session$sendInputMessage函数而不使用try

这是一个例子,当我点击按钮时,我创建了dummy_data来更新所有输入,结构应该类似于你导出的内容:

<强> ui.R

library(shiny)
shinyUI(fluidPage(
  textInput("control_label",
            "This controls some of the labels:",
            "LABEL TEXT"),
  numericInput("inNumber", "Number input:",
               min = 1, max = 20, value = 5, step = 0.5),
  radioButtons("inRadio", "Radio buttons:",
               c("label 1" = "option1",
                 "label 2" = "option2",
                 "label 3" = "option3")),
  actionButton("update_data", "Update")

  ))

<强> server.R

library(shiny)

dummy_data <- c("inRadio=option2","inNumber=10","control_label=Updated TEXT" )

shinyServer(function(input, output,session) {
  observeEvent(input$update_data,{    
    out <- lapply(dummy_data, function(l) unlist(strsplit(l, "="))) 
   for (inpt in out) {
     session$sendInputMessage(inpt[1], list(value=inpt[2]))
    }
   })

})

在调用update之前,所有session$sendInputMessage函数都会预先格式化值。我还没有尝试过所有可能的输入,但至少对于这三个你可以将一个字符串传递给函数来更改numericInput,它仍然可以正常工作。

如果这是某些输入的问题,您可能希望使用reactiveValuesToList(input)保存save,当您想要更新输入时,请使用load并运行列表在for循环中(您必须将其调整为命名列表)。

答案 1 :(得分:3)

这有点旧,但我认为发布一个完整的示例,保存和加载用户输入是有用的。

library(shiny)  

ui <- shinyUI(fluidPage(
  textInput("control_label",
            "This controls some of the labels:",
            "LABEL TEXT"),
  numericInput("inNumber", "Number input:",
               min = 1, max = 20, value = 5, step = 0.5),
  radioButtons("inRadio", "Radio buttons:",
               c("label 1" = "option1",
                 "label 2" = "option2",
                 "label 3" = "option3")),

  actionButton("load_inputs", "Load inputs"), 
  actionButton('save_inputs', 'Save inputs')

)) 

server <-  shinyServer(function(input, output,session) { 

  observeEvent(input$load_inputs,{   

    if(!file.exists('inputs.RDS')) {return(NULL)}

    savedInputs <- readRDS('inputs.RDS')

    inputIDs      <- names(savedInputs) 
    inputvalues   <- unlist(savedInputs) 
    for (i in 1:length(savedInputs)) { 
      session$sendInputMessage(inputIDs[i],  list(value=inputvalues[[i]]) )
    }
  })

  observeEvent(input$save_inputs,{ 
    saveRDS( reactiveValuesToList(input) , file = 'inputs.RDS')
  })  
})

答案 2 :(得分:1)

除非您正在进行大量高度灵活的类型输入(renderUI块可以是任何类型的输入),否则您可以创建一个存储所有当前值的列表,使用dput来保存它们到具有相应dget的文件中以读取它。

在我拥有的一个应用程序中,我允许用户下载存储所有上传数据及其所有选项的文件。

output$saveData <- downloadHandler(
  filename = function() { 
    paste0('Export_',Sys.Date(),'.sprout')
  },
  content = function(file) {
    dataToExport = list()
    #User specified options
    dataToExport$sproutData$transformations=sproutData$transformations  #user specified transformations
    dataToExport$sproutData$processing=sproutData$processing  #user specified text processing rules
    dataToExport$sproutData$sc=sproutData$sc  #user specified option to spell check
    dataToExport$sproutData$scOptions=sproutData$scOptions  #user specified spell check options (only used if spell check is turned on)
    dataToExport$sproutData$scLength=sproutData$scLength  #user specified min word lenght for spell check (only used if spell check is turned on)
    dataToExport$sproutData$stopwords=sproutData$stopwords  #user specified stopwords
    dataToExport$sproutData$stopwordsLastChoice=sproutData$stopwordsLastChoice #last pre-built list selected
    dput(dataToExport,file=file)
  }
)

在这里我创建一个空列表,然后我坚持使用我在我的应用程序中使用的值。 dTE$sD$name结构的原因是我有一个名为reactiveValues的{​​{1}},它存储了所有用户选择的选项和数据。所以,我在输出中保留了结构。

然后,我有一个加载数据页面,它执行以下操作:

sproutData

实际输出是一个表格,详细说明了它找到了什么以及它设置了什么。但是,因为我知道所有输入并且它们没有改变,所以我可以显式地存储它们(默认值或更改的值),然后在上载保存文件时显式更新它们。