19

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 code Commented Oct 26, 2016 at 22:04

1 Answer 1

14

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() } ) }) 
Sign up to request clarification or add additional context in comments.

7 Comments

Ah, very clever. So I take it that there is no way to use r markdown without needing to regenerate the plots? That's unfortunate, but your idea is perfect for a quick fix! Apologies for not including actual code. I'm sure that I'd be violating a number of NDAs by posting the full code to a public website. I was just looking for a general strategy anyway. Thanks!
@agstudy: I am using plotly plots and datatable for the tables. I am unable to use something similar to this for those elements. I get an error "Error in gList: only 'grobs' allowed in "gList". Is there a way I can convert plotly and datatable elements to grobs?
@krish sorry I don't know. I don't use plotly.
@krish Did you ever get this working with plotly and DT?
@DanteSmith How about you?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.