3
votes

I have a shiny app that draw some ggplot2 plots in app. Now I’m making it into a package and export the plot drawing as a function. I found once I draw some ggplot in RStudio and start my shiny app, then all plots in my shiny app went to the RStudio plot pane.

I have tracked down the problem to very specific location and made a minimal working example.

This shiny app draw a ggplot, though it first save the ggplot into png with ggsave, then return the ggplot object to renderPlot so that it is shown in app.

library(shiny)
library(ggplot2)
SAVE_PLOT <- TRUE
ui <- fluidPage(fluidRow(column(12, plotOutput("plot"))))
server <- function(input, output){
  output$plot <- renderPlot({
    g <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
    if (SAVE_PLOT) {
      ggsave("plot.png", g)
    }
    g
  })
}
shinyApp(ui = ui, server = server)

If I run this simple code in RStudio first

library(ggplot2)
ggplot(mtcars, aes(wt, mpg)) + geom_point()

then run the app above, the plot in app is shown in RStudio plot window.

If I set SAVE_PLOT <- FALSE then the app will have plot shown correctly. This show the problem is caused by ggsave.

I didn’t try if plot first and save it later will not have this problem, because the plot in my real app is reactive, plot it first means I need to wrap every plot into a reactive instead of just write in renderPlot.

If I run dev.off after the ggplot in console, the plot will not go to rstudio. Though this is obviously not a solution either.

According to ggsave code, it create a new device then turn off current device after saving. Is it possible that it actually messed up in this case and turned off the shiny plot device also?

It's also possible to be something with Shiny, or with RStudio.

UPDATE: the problem is not specific to RStudio, the R console have same problem.

2
The problem lies in the function ggplot_gtable() that ggsave() calls (via print()). If you build a ggplot graph then things can get messed up in shiny, and I haven't found a workaround. I wrote some really annoying code here (l. 14-25) to address this issue. Basically I force people to close all graphics devices before starting the shiny app.Claus Wilke
Thanks for the confirmation. I'll look at the related code. It should not be like this.dracodoc
I've filed an issue about the bug in ggsave(): github.com/tidyverse/ggplot2/issues/2363. I suspect the issue is not in ggplot_gtable(), but rather in the opening and closing of the device in ggsave().wch
@wch Yes, you are correct. I had experienced a similar problem where I was using ggplot_gtable() and not ggsave(), but in this case ggsave() is the culprit.Claus Wilke

2 Answers

2
votes

Thanks for hints from @Claus Wilke, I don't think we should turn off the current device but we can save the device and restore it.

We can use this to work around the problem:

library(shiny)
library(ggplot2)
SAVE_PLOT <- TRUE
ui <- fluidPage(fluidRow(column(12, plotOutput("plot"))))
server <- function(input, output){
  output$plot <- renderPlot({
    g <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
    if (SAVE_PLOT) {
      cur_dev <- dev.cur()
      ggsave("plot.png", g)
      dev.set(cur_dev)
    }
    g
  })
}
shinyApp(ui = ui, server = server)

Thanks to @wch, issue has been created in ggplot2 repo since it's a ggsave bug.

0
votes

Enclosing the ggsave() function in pdf(NULL); ggsave(...); dev.off() seems to provide a workaround here, though sometimes the graphics device is in strange state when closing the app.

library(shiny)
library(ggplot2)
SAVE_PLOT <- TRUE
ui <- fluidPage(fluidRow(column(12, plotOutput("plot"))))
server <- function(input, output){
  output$plot <- renderPlot({
    g <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
    if (SAVE_PLOT) {
      pdf(NULL)
      ggsave("plot.png", g)
      dev.off()
    }
    g
  })
}
qplot(1:10, 1:10) # shows up in R Studio window
shinyApp(ui = ui, server = server) # shows up in shiny window