0
votes

I have a shiny app with one slider input and one select input. The plot in plotOutput is updated based on these input values. I also have an actionButton which allows me to save the plot using ggsave. An MWE is below:

library(shiny)
library(ggplot2)

# Define UI for application that draws a histogram
ui <- fluidPage(
    tabsetPanel(
        tabPanel('Test',
                 sliderInput("bins",
                             "Number of bins:",
                             min = 1,
                             max = 50,
                             value = 30), # Does nothing for now, will be implemented later
                 selectInput('inputtest', 'Select val:',
                             c('A' = 'a',
                               'B' = 'b',
                               'C' = 'c')),
                 plotOutput("distPlot"),
                 actionButton('testbtn', 'Test me')
                 )
    )
)

server <- function(input, output) {
    output$distPlot <- renderPlot({
        if(input$inputtest=='a') {
            pltitle <- 'Title A'
        } else if(input$inputtest=='b') {
            pltitle <- 'Title B'
        } else {
            pltitle <- 'No more!'
        }
        plt <- qplot(eruptions, waiting, data=faithful) + ggtitle(pltitle)
        print(plt)
        observeEvent(input$testbtn, {
            print(paste('Saving plot', pltitle))
            #ggsave(paste0('Plot-', input$inputtest, '.png'), plt)
        })
        observeEvent(input$inputtest, {
            print(paste('Changed input to', input$inputtest, '| input button was activated', input$testbtn, 'times'))
        })
    })
}
shinyApp(ui = ui, server = server)

The issue I'm facing is, every time I update the value of the select input, actions in observeEvent gets repeated times the number of changes I have made to this input. At app launch, it prints the following message in the console: [1] "Changed input to a | input button was activated 0 times"

Then I change the select input to 'B', it displays:

[1] "Changed input to b | input button was activated 0 times"
[1] "Changed input to b | input button was activated 0 times"

I then change it back to 'A', and the console prints:

[1] "Changed input to a | input button was activated 0 times"
[1] "Changed input to a | input button was activated 0 times"
[1] "Changed input to a | input button was activated 0 times"

If now I click on the action button to export plot, the console prints:

[1] "Saving plot Title B"
[1] "Saving plot Title A"
[1] "Saving plot Title A"

It wants to save plot B first, then twice plot A…

After having clicket on the action button, every time I change the select value another plot is saved in the middle of the repeating print instructions (example if I set select to C):

[1] "Changed input to c | input button was activated 1 times"
[1] "Changed input to c | input button was activated 1 times"
[1] "Changed input to c | input button was activated 1 times"
[1] "Saving plot No more!"
[1] "Changed input to c | input button was activated 1 times"

In my complete app, where the slider input has an action on the plot display and such a plot is saved, it is saved with the default slider value.

That kind of behavior not only increases app processing time, but also saves unwanted plots and sometimes erases plots that I had tuned through the app.

Is this a normal behavior of Shiny, I am misunderstanting something? If yes, how can I get the expected behavior (one change = one update; no unwanted plot saving)?

If no, I imagine that I should file an issue on Shiny repo.

Thanks.

1
Take your observeEvents outside your renderPlot.Limey

1 Answers

1
votes

Y>ou don't want to keep you observer inside the render functions, since this causes an new initialization of an observer every thime the render function is called. I also splited up the code within the render function into two reactive statements so that we can access the at different places.

server <- function(input, output) {
  pltitle <- reactive({
    if(input$inputtest=='a') {
      'Title A'
    } else if(input$inputtest=='b') {
      'Title B'
    } else {
      'No more!'
    }
  })
  plt <- reactive({
    qplot(eruptions, waiting, data=faithful) + ggtitle(pltitle())
    
  })
  
  
  output$distPlot <- renderPlot({
    print(plt())
  })
  observeEvent(input$testbtn, {
    print(paste('Saving plot', pltitle()))
    #ggsave(paste0('Plot-', input$inputtest, '.png'), plt)
  })
  observeEvent(input$inputtest, {
    print(paste('Changed input to', input$inputtest, '| input button was activated', input$testbtn, 'times'))
  })
}
shinyApp(ui = ui, server = server)

the ui function can you leave as it is.