Show That Shiny Is Busy (Or Loading) When Changing Tab Panels

Show that Shiny is busy (or loading) when changing tab panels

Via the Shiny Google group, Joe Cheng pointed me to the shinyIncubator package, where there is a progress bar function that is being implemented (see ?withProgress after installing the shinyIncubator package).

Maybe this function will be added to the Shiny package in the future, but this works for now.

Example:

UI.R

library(shiny)
library(shinyIncubator)

shinyUI(pageWithSidebar(
headerPanel("Testing"),
sidebarPanel(
# Action button
actionButton("aButton", "Let's go!")
),

mainPanel(
progressInit(),
tabsetPanel(
tabPanel(title="Tab1", plotOutput("plot1")),
tabPanel(title="Tab2", plotOutput("plot2")))
)
))

SERVER.R

library(shiny)
library(shinyIncubator)

shinyServer(function(input, output, session) {
output$plot1 <- renderPlot({
if(input$aButton==0) return(NULL)

withProgress(session, min=1, max=15, expr={
for(i in 1:15) {
setProgress(message = 'Calculation in progress',
detail = 'This may take a while...',
value=i)
print(i)
Sys.sleep(0.1)
}
})
temp <- cars + matrix(rnorm(prod(dim(cars))), nrow=nrow(cars), ncol=ncol(cars))
plot(temp)
})

output$plot2 <- renderPlot({
if(input$aButton==0) return(NULL)

withProgress(session, min=1, max=15, expr={
for(i in 1:15) {
setProgress(message = 'Calculation in progress',
detail = 'This may take a while...',
value=i)
print(i)
Sys.sleep(0.1)
}
})
temp <- cars + matrix(rnorm(prod(dim(cars))), nrow=nrow(cars), ncol=ncol(cars))
plot(temp)
})
})

Shiny can not display different webGLOutput on different tab panels?

You appear to be using shinyRGL. Don't use it, rgl has what you need. Here's an example that works for me:

ui.R:

library(shiny)

shinyUI(fluidPage(
mainPanel(
tabsetPanel(
tabPanel("red",
rglwidgetOutput('thewidget1')),
tabPanel("green",
rglwidgetOutput('thewidget2'))
))
))

server.R:

library(shiny)
library(rgl)

options(rgl.useNULL = TRUE)
shinyServer(function(input, output, session) {

x <- rnorm(100)
y <- 2*rnorm(100)
z <- 10*rnorm(100)
open3d()
plot3d(x, y, z, col = "red")
scene1 <- scene3d()
plot3d(z, y, x, col = "green")
scene2 <- scene3d()
rgl.close()

save <- options(rgl.inShiny = TRUE)
on.exit(options(save))

output$thewidget1 <- renderRglwidget(
rglwidget(scene1)
)

output$thewidget2 <- renderRglwidget(
rglwidget(scene2)
)

})

By the way, we would have got here a lot sooner if you had posted a reproducible example as requested.

Show text in shiny app when tabpanel is clean and hide it when output is displayed

Here is my take on the example shiny app:

library(shiny)
library(shinyjs)

# Define UI for application that draws a histogram
ui <- fluidPage(
useShinyjs(),

# Application title
titlePanel("Old Faithful Geyser Data"),

# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
actionButton("submit", "Show plot")
),

# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
tabPanel(
"Plot",
uiOutput("help_text"),
plotOutput("distPlot")
)
)
)
)
)

# Define server logic required to draw a histogram
server <- function(input, output) {

output$help_text <- renderUI({
HTML("<b>Click 'Show plot' to show the plot.</b>")
})

plot_data <- eventReactive(input$submit, {

hide("help_text")

# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)

# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})

output$distPlot <- renderPlot({
plot_data()
})
}

# Run the application
shinyApp(ui = ui, server = server)

Shiny Plotly Plot stuck when switching tabs quickly

This looks like a bug on Plotly side to me. If you dont assign a starting width, the plot will be 100px wide. Changing the width of the div to 100% doesnt really do much.

You could include some Javascript to resize the plot every time the Tab is clicked, or you could deactivate all Tab buttons as long as shiny is busy.

With the resize method, the plots will be redrawn every time you hit the Tab, and after changing the window size, they resize normally again. I also tried to use the redraw, relayout methods of Plotly without success.

So, I would prefer the 2nd option, to disable the Tabs while the app is busy, but that doesnt really answer your question, so i commented out the JavaScript.

time_waste<- function(magnitude) {
y<-0
for(i in 1:magnitude) {
y<- y + rnorm(1,0,1)
}
return(abs(y))
}

## Resize plot p1 at every Tab click.
js <- HTML("
$(document).on('shiny:value', function() {
$('#tabset li a').on('click',function() {
Plotly.Plots.resize('p1');
});
});
"
)

## Deactivate all Buttons as long as shiny is busy
# js <- HTML('
# $(document).on("shiny:busy", function() {
# var inputs = document.getElementsByTagName("a");
# console.log(inputs);
# for (var i = 0; i < inputs.length; i++) {
# inputs[i].disabled = true;
# }
# });
#
# $(document).on("shiny:idle", function() {
# var inputs = document.getElementsByTagName("a");
# console.log(inputs);
# for (var i = 0; i < inputs.length; i++) {
# inputs[i].disabled = false;
# }
# });'
# )

ui <- fluidPage(
## Include JavaScript to the HTML
tags$head(tags$script(js)),
sidebarLayout(
sidebarPanel(width = 3,
fluidRow(
column(4,
numericInput(
inputId = "magnitude",
label = "magnitude",
value = 1000000
)))),
mainPanel(width = 8,
tabsetPanel(id = "tabset",
tabPanel("Plot1", plotlyOutput("p1", height = "700px")),
tabPanel("Plot2", plotlyOutput("p2", height = "700px"))))
)
)

server<- function(input, output, session) {
y <- reactive({
rep(time_waste(time_waste(input$magnitude)),3)
})

output$p1 <- renderPlotly({
plot_ly(x = c("giraffes", "orangutans", "monkeys"),
y = y(),name = "SF Zoo",type = "bar")
})

output$p2<- renderPlotly({
plot_ly(x = c("giraffes", "orangutans", "monkeys"),
y = y(), name = "SF Zoo",type = "bar")
})
}

shinyApp(ui, server)

shiny - automatically jump into new appended tab

The problem was with the way your tab_id's where set. there must have been some clash.

library(shiny)
library(leaflet)
library(shinydashboard)

pts <- data.frame(
id= letters[seq( from = 1, to = 10 )],
x = rnorm(10, mean = -93.625),
y = rnorm(10, mean = 42.0285)
)

# Define UI
ui <- fluidPage(

tabsetPanel(id='my_tabsetPanel',
tabPanel('Map1',
leafletOutput('map1')
)
)
)

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

output$map1 <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(-93.65, 42.0285, zoom = 6)
})

observe({

input$my_tabsetPanel

tab1 <- leafletProxy('map1', data = pts) %>%
clearMarkers() %>%
addCircleMarkers(lng = ~x, lat = ~y, radius = 4, layerId = ~id)

})

observeEvent(input$map1_marker_click, {
tab_title <- input$map1_marker_click[1]
appendTab(inputId = "my_tabsetPanel",
tabPanel(
tab_title, #paste0("tab_",tab_title),
value = paste0("tab_",tab_title),
fluidRow(

box('test')

#tags$b("some text")
)))

updateTabsetPanel(session, "my_tabsetPanel", selected = paste0("tab_",tab_title))

})

}

shinyApp(ui = ui, server = server)

changing the tab in the shiny

For completeness, here's a example using the awesome fullPage library mentioned in the comments:

# Dependencies install
# install.packages("remotes")
# remotes::install_github("RinteRface/fullPage")

library(shiny)
library(fullPage)

options <- list(
sectionsColor = c('#f2f2e2', '#f2f2f2', '#f2f2f2'),
parallax = TRUE
)

ui <- fullPage(
menu = c("Full Page" = "tab1","Sections" = "tab2","Image" = "tab3"),
opts = options,
fullSection(
center = TRUE,
menu = "tab1",
tags$h1("fullPage.js meets Shiny")
),
fullSection(
menu = "tab2",
fullRow(
fullColumn(
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
fullColumn(
plotOutput(outputId = "distPlot")
)
)
),
fullSectionImage(
menu = "tab3",
img = "https://blog.hubspot.com/hubfs/Smiling%20Leo%20Perfect%20GIF.gif"
)
)

server <- function(input, output){
output$distPlot <- renderPlot({

x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)

hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")

})

}

shinyApp(ui, server)

Although the fullpage.js library that it is used allow some configuration to use left/right keys to navigate, the shiny wrapper library above doesn't seem to expose that setting - I could only disable/enable the keyboard, but not choose direction.

R shiny: display “loading…” message while table is being rendered

Look for the recalculating class instead:

ui <- fluidPage(
actionButton('reload', 'reload'),
dataTableOutput('dtable_out'),
conditionalPanel("$('#dtable_out').hasClass('recalculating')",
tags$div('Loading ... ')
)
)

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

output$dtable_out <- renderDataTable({
input$reload
Sys.sleep(2)
data.frame(a=1:10, b=letters[1:10])
})

}

runApp(list(ui=ui, server=server))

R shiny: display loading... message while function is running

I'm already using a simpler and more reliable way than the one I posted before.

A combination of

tags$style(type="text/css", "
#loadmessage {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
padding: 5px 0px 5px 0px;
text-align: center;
font-weight: bold;
font-size: 100%;
color: #000000;
background-color: #CCFF66;
z-index: 105;
}
")

with

conditionalPanel(condition="$('html').hasClass('shiny-busy')",
tags$div("Loading...",id="loadmessage")
)

Example:

runApp(list(
ui = pageWithSidebar(
headerPanel("Test"),
sidebarPanel(
tags$head(tags$style(type="text/css", "
#loadmessage {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
padding: 5px 0px 5px 0px;
text-align: center;
font-weight: bold;
font-size: 100%;
color: #000000;
background-color: #CCFF66;
z-index: 105;
}
")),
numericInput('n', 'Number of obs', 100),
conditionalPanel(condition="$('html').hasClass('shiny-busy')",
tags$div("Loading...",id="loadmessage"))
),
mainPanel(plotOutput('plot'))
),
server = function(input, output) {
output$plot <- renderPlot({ Sys.sleep(2); hist(runif(input$n)) })
}
))

tags$head() is not required, but it's a good practice to keep all the styles inside head tag.

How to prevent user from doing anything on shiny app when app is busy

After analyzing yours answers and think more about it, I think I found the simpliest solution.

On "shiny busy" event, I display a div in the conditional panel which is 100% of the screen and on first plan, so it prevents any click on the inputs / outputs behind it. When the app is not busy anymore, the panel disappear. The panel is transparent so the user doesn't see it.

Also, it enables me to disable all inputs and output without being dependant of a timer, only on if the app is busy or not.

library(shiny)

ui <- fluidPage(

# spinner css
tags$head(
tags$style(HTML("
#loadmessage {
position:fixed; z-index:8; top:50%; left:50%; padding:10px;
text-align:center; font-weight:bold; color:#000000; background-color:#CCFF66;
}

.loader {
position:fixed; z-index:8; border:16px solid #999999;
border-top: 16px solid #8B0000; border-radius: 50%;
width: 120px; height: 120px; top:45%; left:45%;
animation: spin 2s linear infinite;
}

.prevent_click{
position:fixed;
z-index:9;
width:100%;
height:100vh;
background-color: transpare'nt;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}"))
),

# display load spinner when shiny is busy
conditionalPanel(
condition = "$(\'html\').hasClass(\'shiny-busy\')",
tags$div(class = "loader"),
tags$div(class = "prevent_click")
),
actionButton(
inputId = "increment",
label = "Increment"
),
textOutput("result"),
actionButton(
inputId = "busy",
label = "Busy app"
)
)

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

rv <- reactiveValues(counter = 0)

#increment counter
observeEvent(input$increment,{
rv$counter = rv$counter + 1
})

#display incremented counter
output$result <- renderText({
rv$counter
})

observeEvent(input$busy, {
Sys.sleep(5)
# during this time, the user should not be able to do anything on the app
})
}

shinyApp(ui = ui, server = server)


Related Topics



Leave a reply



Submit