15
votes

I have a large Shiny application that has a number of prompts, then generates tables and plot based on those inputs. I don't use rmarkdown or knitr or anything to format the output. I just use the standard Shiny elements (sidebarPanel, mainPanel, etc.). For the plots and tables I use the standard reactive renderPlot and renderTable objects. I'm looking for an easy way to have a button called "Export to PDF" that exports the elements on the page to a PDF document.

I've looked into using knitr and rmarkdown to generate a document with some fancy formatting (see here and here for examples). The problem is that it appears that I'll need to regenerate the tables and plots either within the Rmd file or the server.R within a downloadHandler object, and I'd like to avoid that.

Is there any way to output the page as a pdf more easily. More specifically, is there any way to directly reference the output tables and plots (i.e. the output$ objects) from within the Rmd file so that plots and tables don't need to be generated twice.

Edit: Here is some simplified code. Note getDataset() is a reactive function that queries a database based on the inputs. My goal is to simply add an "Export" button that exports the already-generated plots and table. (Also as a side note, is there any way I can get a reactive dataset that is shared among all reactive elements? i.e. not need to have ds <- getDataset() in every object?)

Server

output$hist <- renderPlot({
  ds <- getDataset()
  # do data transformations

  ggplot(ds, aes(val)) +
    geom_histogram(binwidth = binSize, aes(fill = ..count..)) +
    labs(title = "val dist", x = "val", y = "Count") + 
    scale_fill_gradient("Count", low = "green", high = "red", guide = FALSE) +
    scale_x_continuous(limits = c(min(ds$val), quantile(ds$val, 0.99))) +
    geom_hline(yintercept=maxY, linetype=3)
})

output$time <- renderPlot({
  ds <- getDataset()
  # do data transformations
  ggplot(ds, aes(as.POSIXlt(unixTime, origin="1970-01-01", tz="UTC"), val), colour = val) +
    scale_y_continuous(limits = c(min(ds$val), quantile(ds$val, 0.99))) +
    labs(title = "Val Over Time", x = "Time (UTC)", y = "val (ms)") +
    geom_point(alpha = 0.3, size = 0.7) +
    geom_smooth()
})
output$stats <- renderTable({
  statsDf = getDataset()
  # do data transformations
  statsDf
})

UI

ui <- fluidPage(
  titlePanel("Results"),

  sidebarLayout(
    sidebarPanel(
      dateInput("startDateTime", "Start Date:", value = "2016-10-21"),
      textInput("startTime", "Start Time", "00:00:00"),
      br(),
      dateInput("endDateTime", "End Date:", value = "2016-10-21"),
      textInput("endTime", "End Time", value = "02:00:00"),
      br(),
      submitButton("Submit")
    ),
    mainPanel(
      tabsetPanel(type = "tabs",
                  tabPanel("Plots",
                           plotOutput("hist"),
                           plotOutput("time"),
                  tabPanel("Statistics", tableOutput("stats"))
      )
    )
  )
)
1
I'm using ggplot. I'll edit the post and put in some sample codeBat Masterson

1 Answers

12
votes

First of all , you should really produce a reproducible example not just a sample of your code. We should copy and paste your code and it will run.

The idea

  1. Since you are using ggplot2 which is king of grid plots, I think one easy option to save plots/tables is to use gridExtra package. Using grid.arrange or arrangeGrobs you can save your grobs to predefined device. Then, downloadhandler will do the download.

  2. To not regenerate all the plots each time, I think one solution is to save them in a global variable that you update each time you change the plot. Here reactiveValues come in rescue to store plots and tables ad dynamic variable.

Solution

ui.R

library(shiny)

shinyUI(fluidPage(

  # Application title
  titlePanel("Save ggplot plot/table without regenration"),

  # Sidebar with a slider input for number of bins
  sidebarLayout(
    sidebarPanel(
      downloadButton('export')
    ),

    # Show a plot of the generated distribution
    mainPanel(
      plotOutput("p1"),
      plotOutput("p2"),
      tableOutput("t1")
    )
  )
))

server.R

library(shiny)
library(ggplot2)
library(gridExtra)

shinyServer(function(input, output) {
  ## vals will contain all plot and table grobs
  vals <- reactiveValues(p1=NULL,p2=NULL,t1=NULL)

  ## Note that we store the plot grob before returning it 
  output$p1 <- renderPlot({
    vals$p1 <- qplot(speed, dist, data = cars)
    vals$p1
  })

  output$p2 <- renderPlot({
    vals$p2 <- qplot(mpg, wt, data = mtcars, colour = cyl)
    vals$p2
  })
  ## same thing for th etable grob
  output$t1 <- renderTable({
    dx <- head(mtcars)
    vals$t1 <- tableGrob(dx)
    dx
  })
  ## clicking on the export button will generate a pdf file 
  ## containing all grobs
  output$export = downloadHandler(
    filename = function() {"plots.pdf"},
    content = function(file) {
     pdf(file, onefile = TRUE)
     grid.arrange(vals$p1,vals$p2,vals$t1) 
     dev.off()
    }
  )
})