2

The main goal of my Shiny app is to display large amounts of data through (interactive) ggplots. With sufficient data, the time it takes to display the plots can run up to ~10 seconds, and I would like to display a progress bar to provide feedback.

I've tried both withProgress and winProgressBar, but neither reflect the time it takes for the ggplots to appear: both progress bars disappear long before the actual plots are shown.

So my question is: how do I implement (any type of) progress bar to reflect the time it takes for the ggplots to appear on screen?

library(shiny)
library(ggplot2)
library(dplyr)

ui = fluidPage(
    mainPanel(
      uiOutput("plots")
    )
)

server = function(input, output) {

  #list of things I want to plot, which will be split over column wt
  plotlist = sort(unique(mtcars$wt))[1:4]

  observe({
    pb = winProgressBar(                                 #test 1: winProgressBar
      title = 'observe',                                 #test 1: winProgressBar
      label = 'plotting'                                 #test 1: winProgressBar
    )                                                    #test 1: winProgressBar
    message({                                            #test 1: winProgressBar

      withProgress(message = 'ggplotting', value = 0, {  #test 2: withProgress

        for (i in plotlist) local({

          nm <- i
          temp.data <- filter(mtcars, wt == plotlist[nm])

          plotAname  <- paste0("plotA", nm)
          output[[plotAname]] <- renderPlot(ggplot(temp.data, aes(x = mpg, y= cyl)) + geom_point())

          plotBname  <- paste0("plotB", nm)
          output[[plotBname]] <- renderPlot(ggplot(temp.data, aes(x = mpg, y= drat)) + geom_point())

          plotCname  <- paste0("plotC", nm)
          output[[plotCname]] <- renderPlot(ggplot(temp.data, aes(x = mpg, y= disp)) + geom_point())

          plotDname  <- paste0("plotD", nm)
          output[[plotDname]] <- renderPlot(ggplot(temp.data, aes(x = mpg, y= hp)) + geom_point())

          setWinProgressBar(pb, value = nm/10)         #test 1: winProgressBar
          incProgress(1/(length(plotlist)))            #test 2: withProgress
        }) #end of for()
      }) #end of withProgress                          #test 2: withProgress

    close(pb)                                          #test 1: winProgressBar
    }) #end of message                                 #test 1: winProgressBar
  }) #end of observe

  output$plots <- renderUI({

    withProgress(message = 'rendering', value = 0, {   #test 3: withProgress

      plot_output_list <- lapply(plotlist, function(i) { 

        incProgress(1/(length(plotlist)))              #test 3: withProgress

        #encompass everything in a div because lapply can only returns a single result per loop cycle. 
        div(style = "padding: 0px; margin: 0px;",
            div(style = "position:relative; margin-bottom: -5px; padding: 0px;",
                plotOutput(paste0("plotA", i))
            ),
            div(style = "position:relative; margin-bottom: -5px; padding: 0px;",
                plotOutput(paste0("plotB", i))
            ),
            div(style = "position:relative; margin-bottom: -5px; padding: 0px;",
                plotOutput(paste0("plotC", i))
            ),
            plotOutput(paste0("plotD", i))
        )
      }) #end of lapply
    }) #end of withProgress                          #test 3: withProgress
  }) #end of output$plots

}

shinyApp(ui = ui, server = server)

This example takes about ~4 seconds to display its plots. All three test progress bars have finished after about ~1 second.

Thanks for taking the time to go through this! If I can provide any clarification, please let me know.

Siebren
  • 33
  • 5
  • When running your code, I got an error because of the winProgressBar function. It seems it was removed from the utils package. – jess Nov 07 '18 at 11:42
  • No error here! I'm currently running utils v3.5.1. You can comment out any line with comment "test 1" and it should work again. – Siebren Nov 07 '18 at 11:52
  • fyi: I get the error with utils 3.4.4. – jess Nov 07 '18 at 12:00

1 Answers1

2

It's not actually a progress bar as you'd like to generate. But you can display a loading message within a banner instead, which is why I suppose it could be useful here. Just copy the following code-snippet into the ui-part of your app and adjust the colors as needed.

info_loading <- "Shiny is busy. Please wait."
your_color01 <- # define a color for the text
your_color02 <- # define a color for the background of the banner

tags$head(tags$style(type="text/css",
                                      paste0("
                                             #loadmessage {
                                             position: fixed;
                                             top: 0px;
                                             left: 0px;
                                             width: 100%;
                                             padding: 5px 0px 5px 0px;
                                             text-align: center;
                                             font-weight: bold;
                                             font-size: 100%;
                                             color: ", your_color01,";
                                             background-color: ", your_color02,";
                                             z-index: 105;
                                             }
                                             "))),
                 conditionalPanel(condition="$('html').hasClass('shiny-busy')",
                                  tags$div(info_loading,id="loadmessage"))

Don't hesitate to adjust the parameters (e.g. top position) as needed. You may further see: shiny loading bar for htmlwidgets

alex_555
  • 1,092
  • 1
  • 14
  • 27
  • Thank you for the suggestion! Your solution works in the example code, and works most of the time in my app. One situation where it does not work is when the list of plots increases (in the example, this would be when plotlist becomes longer). In these instances, the info_loading message only appears when the user scrolls down to the space where the plot will appear... Would this have to do with tags$head? – Siebren Nov 07 '18 at 12:20
  • I can't give you a concrete answer here, as I never encountered this issue before. Do you mean that it only appears during scrolling? Or does it appear just on top of the screen and thus can't be seen? You may try to adjust the position argument of the banner. `tags$head` might have to do with this issue, but I'm not sure which tag would be better suited here. You could play around with different tags and see what happens (see: https://shiny.rstudio.com/articles/tag-glossary.html) – alex_555 Nov 07 '18 at 12:52
  • 1
    It was indeed due to the `position` parameter! Thanks again :) – Siebren Nov 07 '18 at 13:22