I have a shiny app that runs a simulation. The goal is to show the user the calculation steps in between as a plot.
How do I force shiny to update the plot?
An MWE would look like this
library(shiny)
server <- function(input, output, session) {
# base plot as a placeholder
output$myplot <- renderPlot(plot(1:1, main = "Placeholder"))
# wait until the button is triggered
observeEvent(input$run, {
print("Do some calculations in 3 steps")
for (i in seq_len(3)) {
print("Do some calculations")
# ...
x <- seq_len(i * 100)
y <- (x + 1)^2 - 1 # this will do for now
print("Plot the data ")
# ISSUE HERE!
# this should render the current step of the simulation, instead it
# renders only after the whole code is run (i.e., after step 3)
output$myplot <- renderPlot(plot(x, y, main = sprintf("Round %i", i), type = "l"))
print("Wait for 1 second for the user to appreciate the plot...")
Sys.sleep(1)
}
})
}
ui <- fluidPage(
actionButton("run", "START"),
plotOutput("myplot")
)
shinyApp(ui = ui, server = server)
The issue is, that shiny runs the code and produces one plot at the end of the simulation, however, I want to get a plot at each simulation step (displayed for at least one second).
Any help/hint is greatly appreciated.
Appendix
I have looked at this post, but replacing the text with a plot/renderPlot doesn't yield the correct results.
You could nest an observer into an observeEvent to make it work. Based on Jeff Allen's code from the SO topic you linked.
Crucial part:
observeEvent(input$run, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
Full code:
library(shiny)
server <- function(input, output, session) {
rv <- reactiveValues(i = 0)
maxIter <- 3
output$myplot <- renderPlot( {
if(rv$i > 0) {
x <- seq_len(rv$i * 100)
y <- (x + 1)^2 - 1 # this will do for now
plot(x, y, main = sprintf("Round %i", rv$i), type = "l")
} else {
plot(1:1, main = "Placeholder")
}
})
observeEvent(input$run, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
}
ui <- fluidPage(
actionButton("run", "START"),
plotOutput("myplot")
)
shinyApp(ui = ui, server = server)
Related
I'm using rhandsontable in a shiny app to manually update a dataframe to be displayed with ggplot2.
When adding/changing values in the rhandsontable in quick succession, the table starts an infinite loop, which immobilizes the entire shiny application.
Below an example. By quickly changing the values in the table, the app gets stuck.
library(shiny)
library(rhandsontable)
library(ggplot2)
ui <- fluidPage(
rHandsontableOutput("hot"),
plotOutput("plot")
)
server <- function(input, output, session) {
reactive.table <- reactiveValues(values = data.frame(x = c(1,2), y = c(1,2)))
observe({
if (!is.null(input$hot)) {
reactive.table$values <<- hot_to_r(input$hot)
}
})
output$hot <- renderRHandsontable({
rhandsontable(reactive.table$values)
})
output$plot <- renderPlot({
Sys.sleep(2)
ggplot(reactive.table$values, aes(x = x, y = y)) +
geom_point()
})
}
shinyApp(ui = ui, server = server)
Is there a way to stop the infinite loop or to disable further inputs until the plot is rendered?
Thanks.
I don't know how to stop rhandsontable from starting the infinite loop, but you can avoid inputs in quick succession altogether.
Taking your reprex, I restrict changes to be at least 1 second apart, and it works just fine.
library(shiny)
library(rhandsontable)
library(ggplot2)
ui <- fluidPage(
rHandsontableOutput("hot"),
plotOutput("plot")
)
server <- function(input, output, session) {
reactive.table <- reactiveValues(values = data.frame(x = c(1,2), y = c(1,2)))
rv_timer <- reactiveValues(
prev = NULL, current = NULL
)
observe({
if (!is.null(input$hot)) {
# if it's first time editing table:
if (is.null(rv_timer$prev)) {
rv_timer$prev <- Sys.time()
reactive.table$values <- hot_to_r(input$hot)
return(NULL)
}
# if it's not the first time to edit table, get current clock time:
rv_timer$current <- Sys.time()
# if the difference btwn prev recorded time and current time is less
# than 1second, don't do anything, just return:
if ((rv_timer$current - rv_timer$prev) < 1) {
return(NULL)
}
# otherwise proceed as normal:
reactive.table$values <- hot_to_r(input$hot)
# finally set current clock time as `rv_timer$prev` for use in the next
# invalidation:
rv_timer$prev <- Sys.time()
}
})
output$hot <- renderRHandsontable({
rhandsontable(reactive.table$values)
})
output$plot <- renderPlot({
Sys.sleep(2)
ggplot(reactive.table$values, aes(x = x, y = y)) +
geom_point()
})
}
shinyApp(ui = ui, server = server)
Might be a bug in {rhansontable}.
I wonder if someone can explain the behavior of the progressBar().
I have trimmed my shiny app to the bare minimum to reproduce this post.
Now to the problem. When I select "AllRuns", the progress bar pops up and then goes away
before the graphic is displayed. But when I select "scatter", the progress bar nicely waits
until the scatter plot is displayed on the main panel. Is this a normal behavior?
How can I make the progress bar wait until the graphic displays when "AllRuns" is selected?
UPDATE The dataset can be read into R from google docs. it takes about 20 seconds to load into R.
library(shiny)
library(tidyverse)
library(DT)
library(data.table)
final <- fread("https://docs.google.com/spreadsheets/d/170235QwbmgQvr0GWmT-8yBsC7Vk6p_dmvYxrZNfsKqk/pub?output=csv")
runs<- c("AllRuns","scatter")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "run",
label = "Chinook Runs",
choices = runs,
selected = "AllRuns"),
sliderInput(inputId = "Yearslider",
label="Years to plot",
sep="",
min=2000,
max=2014,
value=c(2010,2012))),
mainPanel(
plotOutput("plot")
)))
server <- function(input, output,session) {
session$onSessionEnded(function() {
stopApp()
})
plot_all <- reactive({
final[final$year >= input$Yearslider[1] & final$year <= input$Yearslider[2], ]
})
plotscatter <- reactive({
rnorm(100000)
})
dataInput <- reactive({
if (input$run == "AllRuns") {
plot_all()
}else{
plotscatter()
}
})
# Plot data
create_plots <- reactive({
withProgress(message="Creating graphic....",value = 0, {
n <- 10
for (i in 1:n) {
incProgress(1/n, detail = input$run)
Sys.sleep(0.1)
}
#Make the plots
theme_set(theme_classic())
switch(input$run,
"AllRuns" = ggplot(plot_all(),aes(SampleDate,Count,color = race2)) +
geom_point() + theme_bw() +
labs(x="",y="Number in thousands",title="All Salmon Runs combined"),
"scatter" = plot(plotscatter(),col="lightblue")
)
})#Progress bar closing brackets
})#create_plots closing brackets
output$plot <- renderPlot({
create_plots()
})
}
# Run the application
shinyApp(ui = ui, server = server)
It is simple that the progress bar is updated by the for-loop and the plot code only run after the for-loop. So the progress-bar reach the end, then plot code started. This kind of progress-bar would work if you are process something along with for-loop for example
list_of_files # assume you have a list of data file to read and process
max_progress <- length(list_of_files)
withProgress(message="Creating graphic....",value = 0, {
for (i in 1:max_progress) {
data <- read_csv(list_of_files[i])
... # doing something here
# once the processing code done next line of code will update the progress bar
incProgress(1/n, detail = input$run)
}
})
If you want to display loading one way to do it is using shinycssloaders::withSpinner() on the UI part which would show an animation of loading while UI is updating by server side.
The withProgress would be more useful when you have a list of items to process.
library(shiny)
library(tidyverse)
library(DT)
library(data.table)
library(shinycssloaders)
final <- fread("https://docs.google.com/spreadsheets/d/170235QwbmgQvr0GWmT-8yBsC7Vk6p_dmvYxrZNfsKqk/pub?output=csv")
runs<- c("AllRuns","scatter")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "run",
label = "Chinook Runs",
choices = runs,
selected = "AllRuns"),
sliderInput(inputId = "Yearslider",
label="Years to plot",
sep="",
min=2000,
max=2014,
value=c(2010,2012))),
mainPanel(
withSpinner(plotOutput("plot"))
)))
server <- function(input, output,session) {
session$onSessionEnded(function() {
stopApp()
})
plot_all <- reactive({
final[final$year >= input$Yearslider[1] & final$year <= input$Yearslider[2], ]
})
plotscatter <- reactive({
rnorm(100000)
})
dataInput <- reactive({
if (input$run == "AllRuns") {
plot_all()
}else{
plotscatter()
}
})
# Plot data
create_plots <- reactive({
#Make the plots
theme_set(theme_classic())
switch(input$run,
"AllRuns" = ggplot(plot_all(),aes(SampleDate,Count,color = race2)) +
geom_point() + theme_bw() +
labs(x="",y="Number in thousands",title="All Salmon Runs combined"),
"scatter" = plot(plotscatter(),col="lightblue")
)
})#create_plots closing brackets
output$plot <- renderPlot({
create_plots()
})
}
# Run the application
shinyApp(ui = ui, server = server)
Does Shiny can detect only common R’s objects? If yes, What objects can it observe?
For example, I tried many options with no success to detect a data.tree changes in shiny.
Does anyone know why this happens?
library(shiny)
library(data.tree)
data(acme)
ui <- fluidPage(
actionButton("go", "go" ),
tags$h2("text"),
verbatimTextOutput("text"),
tags$h2("text0"),
verbatimTextOutput("text0"),
tags$h2("text1"),
verbatimTextOutput("text1"),
tags$h2("text2"),
verbatimTextOutput("text2"),
tags$h2("text3"),
verbatimTextOutput("text3"),
tags$h2("text4"),
verbatimTextOutput("text4")
)
server <- function(input, output, session) {
anum <- reactiveValues(a = 0)
a <- reactiveValues(acme = acme, f = NULL)
b <- reactiveVal(acme)
cc <- reactive(a$acme)
observeEvent(input$go, {
z = sample(x = 1:100 , size = 1)
a$cach <<- a$acme$clone()
anum$a <<- anum$a + 1
a$acme$AddChild(paste0("New", z))
a$f <<- a$acme
b(a$acme)
print("a$acme")
print(a$acme)
print("b()")
print(b())
})
### not working
output$text = renderPrint( print(a$f) )
output$text0 = renderPrint(print(b()))
output$text1 = renderPrint(print(cc()))
### working
observe({
print(identical(a$acme, a$cach))
output$text2 = renderPrint(print(b()))
})
### working
observe({
anum$a
output$text3 = renderPrint(print(a$acme))
})
### working
observeEvent(eventExpr = anum$a, handlerExpr = {
output$text4 = renderPrint(print(a$acme))
})
}
shinyApp(ui, server)
Turns out that adding:
a$f <- 0 #to force reaction
a$f <- a$acme
a$acme <- 0 #to force reaction
a$acme <- a$f
'fixed' the problem.
library(shiny)
library(data.tree)
data(acme)
ui <- fluidPage(
actionButton("go", "go" ),
verbatimTextOutput("text"),
verbatimTextOutput("text1"),
verbatimTextOutput("text2"),
verbatimTextOutput("text3"),
verbatimTextOutput("text4")
)
server <- function(input, output, session) {
anum <- reactiveValues(a = 0)
a <- reactiveValues(acme = acme, f = NULL)
b <- reactiveVal(acme)
cc <- reactive(a$acme)
observeEvent(input$go, {
z <- sample(x = 1:100 , size = 1)
a$cach <- a$acme$clone()
anum$a <- anum$a + 1
a$acme$AddChild(paste0("New", z))
a$f <- 0 #to force reaction
a$f <- a$acme
a$acme <- 0 #to force reaction
a$acme <- a$f
b(a$acme)
print("a$acme")
print(a$acme)
print("b()")
print(b())
})
### not working
output$text = renderPrint({
req(a$f)
print(a$f)})
output$text2 = renderPrint(print(cc()))
## now it works
observe({
print(identical(a$acme, a$cach)) #this is triggering the update
output$text1 = renderPrint(print(b()))
})
### working
observe({
anum$a
output$text3 = renderPrint(print(a$acme))
})
### working
observeEvent(eventExpr = anum$a, handlerExpr = {
output$text4 = renderPrint(print(a$acme))
})
}
shinyApp(ui, server)
I think there's something going on with a$acme$AddChild(paste0("New", z)) method that is not detected as a change when called.
It is inquiry about Rshiny.
I created a function called foo as shown below.
In the function, there are 5 plots in the for loop. When you make a plot in Shiny, only the last plot is visible, and the remaining plots are not visible. Can not you see that five plots are being created (updated)?
foo <- function(iter = 5){
for(j in 1:iter){
plot(iris$Sepal.Length, iris$Sepal.Width, col = j)
Sys.sleep(0.5)
}
}
ui<-shinyUI(fluidPage(
sth
plotOutput('myplot')
))
server <- shinyServer(function(input, output, session){
sth ...
output$myplot <- renderPlot({
f <- foo(iter = 3)
})
})
})
You can't use a loop here, because the server executes all of the code before rendering new output in the UI, and Sys.sleep() just causes the entire R process to stop for the specified amount of time. Instead, you can use invalidateLater() to make your plotting function trigger at set intervals of time, while still allowing the rest of the program to run normally.
library(shiny)
ui <- shinyUI(fluidPage(
sliderInput("iterations", "Iterations", 1, 10, 3),
sliderInput("interval", "Interval (ms)", 100, 1000, 500, step = 100),
actionButton("draw", "Draw"),
plotOutput('myplot')
))
server <- shinyServer(function(input, output, session) {
foo <- function(iterations = 5, interval = 500) {
i <- 0
output$myplot <- renderPlot({
i <<- i + 1
if (i < iterations)
invalidateLater(interval)
plot(iris$Sepal.Length, iris$Sepal.Width, col = i)
})
}
observeEvent(input$draw, foo(input$iterations, input$interval))
})
shiny::shinyApp(ui, server)
Now, you could also wrap this idea of doing something every interval into a sort of delayed map function, looking something like this:
map_later <- function(.x, .f, ..., .interval = 500) {
i <- 0
observe({
i <<- i + 1
if (i < length(.x))
invalidateLater(.interval)
.f(.x[i], ...)
})
}
That would produce a neater and more easily manageable server:
ui <- shinyUI(fluidPage(
plotOutput('myplot')
))
server <- shinyServer(function(input, output, session) {
map_later(1:5, function(i) {
output$myplot <- renderPlot({
plot(iris$Sepal.Length, iris$Sepal.Width, col = i)
})
}, .interval = 500)
})
shiny::shinyApp(ui, server)
Naming is probably not great here, but hey, it does what it's supposed to.
I have two outputs, a print and a plot. I would like to execute the print after the run button is pressed (working) and then when the print completes the plot part executes.
The reason for this is the print part does some calculations that take a few minutes and the output from that needs to go to the plot command.
Simple example:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton('run','Run')
),
mainPanel(
verbatimTextOutput("Descriptive"),
plotOutput("plotData",width = "700px", height = "500px")
)
)
)
server <- function(input, output) {
output$Descriptive <- renderPrint({
if(input$run>0){
return(isolate({
cat('Number of rows:', nrow(mtcars))
mpg2 <<- mtcars$mpg+3
cyl2 <<- mtcars$cyl+3
}))
}else{return(invisible())}
})
#### RUN AFTER DESCRIPTIVE COMPLETES ####
output$plotData <- renderPlot({
plot(mpg2,cyl2)
})
}
shinyApp(ui = ui, server = server)
I would suggest you to store the variable as reactiveValues and make the plot dependent on them. By this you can avoid the current global assignment and also make the plot update dependent on a change in its variables.
It could look like this:
global <- reactiveValues(mpg2 = mtcars$mpg, cyl2 = mtcars$cyl, txt = "")
observe({
if(input$run > 0){
Sys.sleep(5) # simulate minutes of calculating
global$txt <- paste('Number of rows:', nrow(mtcars))
global$mpg2 <- mtcars$mpg + 3
global$cyl2 <- mtcars$cyl + 3
}
})
Your app would look like this:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton('run','Run')
),
mainPanel(
verbatimTextOutput("Descriptive"),
plotOutput("plotData",width = "700px", height = "500px")
)
)
)
server <- function(input, output) {
global <- reactiveValues(mpg2 = mtcars$mpg, cyl2 = mtcars$cyl, txt = "")
observe({
if(input$run > 0){
Sys.sleep(5) # simulate minutes of calculating
global$txt <- paste('Number of rows:', nrow(mtcars))
global$mpg2 <- mtcars$mpg + 3
global$cyl2 <- mtcars$cyl + 3
}
})
output$Descriptive <- renderPrint({
if(nchar(global$txt)) return(global$txt)
})
#### RUN AFTER DESCRIPTIVE COMPLETES ####
output$plotData <- renderPlot({
plot(global$mpg2, global$cyl2)
})
}
shinyApp(ui = ui, server = server)