在处理数据时更新闪亮的界面

Update shiny interface while processing data

如何在处理数据的同时更新界面?

这是完整的故事:

我正在 运行 在 docker 容器中安装一个闪亮的应用程序。在启动该应用程序之前,我需要下载大量数据(这需要一些时间)。在此期间,无法连接到闪亮的网络服务器——因为它还没有启动。但是,我想制作一个简单的页面“请稍候,正在下载数据 (XX%)”或类似的内容,一旦数据加载完毕,停止该应用程序并 运行 另一个应用程序。

下面是一些有效的代码。也就是说,应用程序 运行s,执行“处理块”,当“数据加载”完成时,调用 stopApp 并返回结果。但是,在完成所有“工作”之前不会呈现输出。

library(shiny)

ui <- fluidPage(
                titlePanel("Please wait!"),
                fluidRow(
                         textOutput("msg")))

srv <- function(input, output) {

  output$msg <- renderText("Waiting")

  observe({
    ## the data loading block
    message("doing the job")
    Sys.sleep(10)

    message("proceeding")
    output$msg <- renderText("OK, proceeding")

    Sys.sleep(10)
    output$msg <- renderText("Nearly finished")
    message("nearly")

    Sys.sleep(10)
    message("done")
    stopApp(list(a=1, b=2))
  })
}

foo <- runApp(shinyApp(ui=ui, server=srv))

现在,我做错了什么?更奇怪的是,即使是“请稍候”renderText 调用也不会在一切完成之前执行。

我觉得是冲水的问题。我假设 shiny 等待与浏览器中的 JS 通信,直到所有反应表达式完成评估。我不知道如何告诉它立即更新 UI。

我想可以将处理拆分为一系列反应式表达式,这些表达式相互依赖并与界面交互,一点一点地更新界面,但首先,这很难扩展(我需要加载多个数据集而且我事先不知道他们的号码)其次它看起来很奇怪。最重要的是,我尝试了几种方法但都失败了。我想出的最佳解决方案是这样的:

ui <- fluidPage(
                titlePanel("Please wait!"),
                fluidRow(
                         numericInput("xxx", label="testlab", value=0),
                         textOutput("msg")))

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

  steps <- reactiveVal(1)

  observe({
    message("starting stuff")
      updateNumericInput(session=session, inputId="xxx", value=1)
  })
    
  observeEvent(input$xxx, {

    n <- req(input$xxx)

    message("Running job ", n)
    Sys.sleep(10)
    updateNumericInput(inputId="xxx", value=n + 1)

    if(n == 3) {
      stopApp(list(steps=n, done=TRUE))
    }
  })

  output$msg <- renderText({
    n <- req(input$xxx)
    return(sprintf("Processing job %d", n))
  })
}

foo <- runApp(shinyApp(ui=ui, server=srv))

有没有我遗漏的简单技巧?

是这样的吗? Source

library(shiny)
library(waiter)

ui <- fluidPage(
  useWaitress(),
  p("App content")
)

server <- function(input, output){

  # call the waitress
  waitress <- Waitress$
    new(theme = "overlay-percent")$
    start() # start

# insert data loading here instead of the for loop
# put some waitress$inc(X) between loading data if your data
# is composed of several variables

  for(i in 1:10){
    waitress$inc(10) # increase by 10%
    Sys.sleep(.3)
  }

  # hide when it's done
  waitress$close() 

}

shinyApp(ui, server)