Shiny学习笔记:用户反馈2

feedback5.gif

对于运行时间比较长的任务,最佳的反馈形式是进度条(progress bar)。不仅提示你运行到哪一步,还帮助你估算需要运行多长时间。本文主要介绍两种方法:Shiny内置的以及包 waiter。不幸的是这些方法都有一个致命的缺点:如果你想使用progress bar,你需要将一个大任务分解成多个小任务,而且每个小任务的运行时间的大致相同,这就很困难了。

Shiny

Shiny提供了进度条的实现方式:

# Create a progress bar object with `Progress$new(max = number_of_steps)`.
progress <- Progress$new(max = 5)

# Display the progress bar by calling the `$set()` method, 
# providing a title for the progress bar in the `message` argument.
progress$set(message = "Starting process")

# Call `$inc()` repeatedly, once for each step.
for (i in 1:5) {
  progress$inc(1)
}

# When done, call `$close()` to terminate the progress bar.
progress$close()

具体在Shinyapp的实现:

ui <- fluidPage(
  numericInput("steps", "How many steps?", 10),
  actionButton("go", "go"),
  textOutput("result")
)

server <- function(input, output, session) {
  data <- reactive({
    req(input$go)
    
    progress <- Progress$new(max = input$steps)
    on.exit(progress$close())
    
    progress$set(message = "Computing random number")
    for (i in seq_len(input$steps)) {
      Sys.sleep(0.5)
      progress$inc(1)
    }
    runif(1)
  })
  
  output$result <- renderText(round(data(), 2))
}

Sys.sleep()替换为自己的脚本就行了

Waiter

Shiny内置的进度条比较单调,Waiter包提供了更加丰富的的实现方式:

第一步:在UI中添加use_waitress()

ui <- fluidPage(
  waiter::use_waitress(),
  numericInput("steps", "How many steps?", 10),
  actionButton("go", "go"),
  textOutput("result")
)

第二步,将Progress替换为Waitress

server <- function(input, output, session) {
  data <- reactive({
    req(input$go)
    waitress <- waiter::Waitress$new(max = input$steps)
    on.exit(waitress$close())
    
    for (i in seq_len(input$steps)) {
      Sys.sleep(0.5)
      waitress$inc(1)
    }
    
    runif(1)
  })
  
  output$result <- renderText(round(data(), 2))
}

默认是在顶部显示细进度条,Waiter提供了多种个性化设置theme

  • overlay:覆盖整个界面的进度条
  • overlay-opacity:半透明的overlay
  • overlay-percent:显示进度百分数的overlay

也可以将进度条的位置设置到某一个input或者output

waitress <- Waitress$new(selector = "#steps", theme = "overlay")

Spinners

很多时间我们无法估计运行时间,只想提醒用户程序正在运行,我们可以使用Spinner,此时使用Waiter

ui <- fluidPage(
  waiter::use_waiter(),
  actionButton("go", "go"),
  textOutput("result")
)

server <- function(input, output, session) {
  data <- reactive({
    req(input$go)
    waiter <- waiter::Waiter$new()
    waiter$show()
    on.exit(waiter$hide())
    
    Sys.sleep(sample(5, 1))
    runif(1)
  })
  output$result <- renderText(round(data(), 2))
}

Waitress一样,也可设置的input或者output显示:

ui <- fluidPage(
  waiter::use_waiter(),
  actionButton("go", "go"),
  plotOutput("plot"),
)

server <- function(input, output, session) {
  data <- reactive({
    req(input$go)
    waiter::Waiter$new(id = "plot")$show()
    
    Sys.sleep(3)
    data.frame(x = runif(50), y = runif(50))
  })
  
  output$plot <- renderPlot(plot(data()), res = 96)
}

waiter包提供了大量的Spinners,还有一个更加简单的方式就是使用包 shinycssloaders,这个包提供了十分实用的函数withSpinner(),只需要用此函数将output封装一下就可以了,目前我几乎所有的Shinyapp都是使用该包实现的:

library(shinycssloaders)

ui <- fluidPage(
  actionButton("go", "go"),
  withSpinner(plotOutput("plot")),
)
server <- function(input, output, session) {
  data <- reactive({
    req(input$go)
    Sys.sleep(3)
    data.frame(x = runif(50), y = runif(50))
  })
  
  output$plot <- renderPlot(plot(data()), res = 96)
}
Researcher

I am a PhD student of Crop Genetics and Breeding at the Zhejiang University Crop Science Lab. My research interests covers a range of issues:Population Genetics Evolution and Ecotype Divergence Analysis of Oilseed Rape, Genome-wide Association Study (GWAS) of Agronomic Traits.

comments powered by Disqus