Shiny学习笔记:用户反馈2
对于运行时间比较长的任务,最佳的反馈形式是进度条(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)
}