Reputation: 378
EDIT
I am building a shiny app and one of the components of it would consist of automated data analysis. My main use for it would be to access an API to gather data and do subsequent analysis on it. This action occurs inside a repeat
loop with a 5 minute delay (although this can be specified by the user of the app). Once the delay is up, the API is accessed again and the process starts all over. This works well for me outside of shiny as I return my plots/tables as a list in the main console.
However, I am not able to execute it within a Shiny app. I cannot provide the API information but for all intents and purposes, here is a replicable example using the mpg
data set.
Below is an example of a plot being generated using an action button and without the use of the repeat loop. Each time you click the action button, the graph will update with the current system time:
and the code that generates this:-
library(shiny)
library(ggplot2)
ui<-fluidPage(
titlePanel('Minimal example'),
tabsetPanel(
tabPanel("Example",
#summary
sidebarPanel(width = 4,
h5("The default interval for the analysis refresh is 5 minutes. If you wish to change this, please do so in the box below:"),
numericInput("intervaltime","Input refresh interval:",5),
br(),
h5("Press 'Run Analysis' button below to start automated analysis"),
actionButton("automatedanalysis", "Run Analysis")),
mainPanel(
h4("An example plot"),
plotOutput("example_plot", width = "100%"),
h4("Some text with updated system time"),
textOutput("example_text")
)
)))
server<-function(input,output,session){
observeEvent(input$automatedanalysis,{
#interval=input$intervaltime*60
#repeat{
currenttime<-Sys.time()
p<-ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point()+ggtitle(paste0("graph made at: ",currenttime))# adds on the current time
output$example_plot<-renderPlot({
return(p)
})
output$example_text<-renderText({
print(paste0("The current system time is: ", Sys.time())) #a check to know that it is working
})
#Sys.sleep(interval)
#}
})
}
shinyApp(ui, server)
However, when I bring the repeat loop into action, with the interval timer which can be toggled using the numeric input in the UI, it no longer works. Here is the non-working code:-
ui<-fluidPage(
titlePanel('Minimal example'),
tabsetPanel(
tabPanel("Example",
#summary
sidebarPanel(width = 4,
h5("The default interval for the analysis refresh is 5 minutes. If you wish to change this, please do so in the box below:"),
numericInput("intervaltime","Input refresh interval:",5),
br(),
h5("Press 'Run Analysis' button below to start automated analysis"),
actionButton("automatedanalysis", "Run Analysis")),
mainPanel(
h4("An example plot"),
plotOutput("example_plot", width = "100%"),
h4("Some text with updated system time"),
textOutput("example_text")
)
)))
server<-function(input,output,session){
observeEvent(input$automatedanalysis,{
interval=input$intervaltime*60
repeat{
currenttime<-Sys.time()
p<-ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point()+ggtitle(paste0("graph made at: ",currenttime))# adds on the current time
output$example_plot<-renderPlot({
return(p)
})
output$example_text<-renderText({
print(paste0("The current system time is: ", Sys.time())) #a check to know that it is working
})
Sys.sleep(interval)
}
})
}
shinyApp(ui, server)
No graphs or textual outputs appear.
Once again, this works fine outside of a Shiny App, but I need this as a feature within the Shiny App I am developing.
In summary, how can I get this to work so that when the action button is clicked, the analysis refreshes after the end of the interval period?
Upvotes: 1
Views: 338
Reputation: 9809
You should take a look at the invalidateLater
or reactiveTimer
functions.
I'm using invalidateLater
in the following example (NOTE: the function takes milliseconds as first argument).
You also shouldn't put any outputs inside an observer, make a reactiveVal
/ reactiveValues
object and fill that at every new interval. You can use that object anywhere in the app then.
I also changed the observeEvent
to a normal observe
, since otherwise it would only trigger when the Button is clicked. Now, the observer triggers, when the Button is clicked, the interval slider is changed and when the interval has passed.
library(shiny)
library(ggplot2)
ui<-fluidPage(
titlePanel('Minimal example'),
tabsetPanel(
tabPanel("Example",
sidebarPanel(width = 4,
h5("The default interval for the analysis refresh is 5 minutes. If you wish to change this, please do so in the box below:"),
numericInput("intervaltime","Input refresh interval:",5),
br(),
h5("Press 'Run Analysis' button below to start automated analysis"),
actionButton("automatedanalysis", "Run Analysis")),
mainPanel(
h4("An example plot"),
plotOutput("example_plot", width = "100%"),
h4("Some text with updated system time"),
textOutput("example_text")
)
)))
server<-function(input,output,session){
rv <- reactiveVal(NULL)
observe({
interval = input$intervaltime*1000
invalidateLater(interval, session)
req(input$automatedanalysis)
print("Fill ReactiveVal")
mpg$hwy <- mpg$hwy * runif(nrow(mpg))
rv(mpg)
})
output$example_plot<-renderPlot({
req(rv())
currenttime<-Sys.time()
print("Plot Code")
ggplot(rv(), aes(displ, hwy, colour = class)) +
geom_point()+ggtitle(paste0("graph made at: ",currenttime))
})
output$example_text<-renderText({
print(paste0("The current system time is: ", Sys.time())) #a check to know that it is working
})
}
shinyApp(ui, server)
Upvotes: 3