download plot as pdf

2,517 views
Skip to first unread message

Luca Fenu

unread,
Feb 19, 2013, 8:42:21 AM2/19/13
to shiny-...@googlegroups.com
Dear Shiny-ers,

once more I return to you to figure out where I am going wrong in my attempts. I have a working script in shiny which generates some plots. The code runs on a 0.3.0 version of shiny, built on R 2.15.2 for 64-bit linux:

shiny /usr/lib64/R/library 0.3.0 NA R (>= 2.14.1) stats, tools, utils, datasets, methods, websockets (>= 1.1.6),
caTools, RJSONIO, xtable, digest NA markdown, Cairo, testthat NA NA GPL-3 2.15.2

I would, now, like to export those same plots to pdf, and offer my users the possibility of downloading them. After reading through the downloadButton/Link documentation, and the few threads in this forum, I've come up with the following setup.

  # generates a path/string for the pdf filename
  myFilename<-reactive(function(){sprintf("/var/tmp/Basename-%s.%s",input$jobName,'pdf')})
  # the plot is generated in a (reactive) function called output$Plot
  # the following lines, when inclued in the reactive function which generate the (lattice) plots, do work fine and produce a pdf in the expected folder.
  # pdf(myFilename(), onefile=T, width=12,height=8)
  # print(MySurfacePlot)
  # dev.off()

This is how I tried to generate the pdf and connect it to the downloadHandler, within server.R

  output$downloadPlot <- downloadHandler(
      filename = myFilename(),
    # a function that actually opens a connection to a pdf and print the result to it
    content = function(FILE=NULL) { # this function must take at least an input variable, namely the filename of the file to save
   pdf(
                   file=FILE, # hopefully this will catch the filename provided in the previous argument of the downloadHandler (filename=myFilename())
                   onefile=T, width=12,height=8)
print(output$Plot)
dev.off()
}
  )

In the ui.R script, I insert the corresponding lines to tackle the downloadButton:

downloadButton('download_P.C_Plot', label = "Download plot as PDF")

When I load the page, the program seems to run fine up to the point where I click on the downloadButton (or Link), which instead of initiating a download opens a new windows where the following error message is printed:

invalid replacement for field ˜.label", should be from class "character" or a subclass (was class "NULL")

I googled the error code and it seems to refer to an issue which has been fixed already - yet, I can't seem to get around it.

Is there something I didn't set correctly? Did I misunderstand the way the downloadHandler function works? Is the /var/tmp an issue for shiny (can it access outside its own app directory?)

Many thanks for any insights.

Luca



Joe Cheng

unread,
Feb 20, 2013, 5:04:43 AM2/20/13
to shiny-...@googlegroups.com
Hi Luca, a few things:

1) The first argument to downloadButton should be "downloadPlot". Also just in general, inputs and outputs must not have "." in their names as this confuses the web browser. (We really ought to have an error/warning...)

2) For downloadHandler(), the first argument should be "filename = myFilename" not "filename = myFilename()". You might also drop "reactive" from around the myFilename function, Shiny will call that function from a reactive context anyway. (Shouldn't matter much either way though.)

3) You can't print(output$Plot), outputs cannot be read in that way. And anyway, even if you could all you'd get is a base64-encoded PNG. You need to re-run the plotting operation inside of that content function (therefore it would be ideal if that plotting operation is in a function that can be accessed by both output$Plot and output$downloadPlot).

4) The myFilename function should only be returning a bare filename, without any directories; its job is to suggest to the browser what the user should save the file as.

5) Your comment "hopefully this will catch the filename..." doesn't reflect what actually happens--but it shouldn't matter. FILE is just the filename of a temp file that Shiny created, it shouldn't matter where it is or what the filename is. Just put your data in there and let Shiny take care of the rest.

Now all that said, there is ALSO the bug that you are currently hitting, which was fixed in GitHub but not released on CRAN yet (the CRAN folks requested that people not update packages more than every 1-2 months). In order to get the fix you will need to run this code:

install.packages("devtools") # if you don't have devtools already
devtools::install_github("shiny", "rstudio")





--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Luca Fenu

unread,
Feb 20, 2013, 9:39:34 AM2/20/13
to shiny-...@googlegroups.com, Luca Fenu
Thanks Jon,

With the suggested changes, the code correctly produces the pdf and sends it to the browser. Many thanks for helping me understand and fix this.
comments in line for the benefit pof others incurring similar problems:

On 20 February 2013 11:04, Joe Cheng <j...@rstudio.com> wrote:
Hi Luca, a few things:

1) The first argument to downloadButton should be "downloadPlot". Also just in general, inputs and outputs must not have "." in their names as this confuses the web browser. (We really ought to have an error/warning...)

Ok, point 1 was a distraction from my side, when I simplified the code to post it here. I did anyway take out all '.' in variable names. 

2) For downloadHandler(), the first argument should be "filename = myFilename" not "filename = myFilename()". You might also drop "reactive" from around the myFilename function, Shiny will call that function from a reactive context anyway. (Shouldn't matter much either way though.)
I've taken out that () - my first attempt was indeed to input just the function name but I was getting back an error.

3) You can't print(output$Plot), outputs cannot be read in that way. And anyway, even if you could all you'd get is a base64-encoded PNG. You need to re-run the plotting operation inside of that content function (therefore it would be ideal if that plotting operation is in a function that can be accessed by both output$Plot and output$downloadPlot).
OK, I created a function with all the plotting code, and which just return the plot upon call. This is then called by the reactivePlot and downloadPlot functions, like this:

# generates a path/string for the pdf filename
myPDF_PCfilename<-reactive(function(){sprintf("/var/tmp/Surface-PC-%s-%s.%s",input$UorE,input$jobName,'pdf')})
   
makePlot <- function() { #... code to make the plot...
  return(justGeneratedPlot)
}

output$PC_Plot <- reactivePlot(function() {
    print(make_PC_Plot())    
})

output$download_PC_Plot <- downloadHandler(
      filename = myPDF_PCfilename,
    # a function that actually opens a connection to a pdf and print the result to it
    content = function(FILE=NULL) {
   pdf(file=FILE, onefile=T, width=12,height=8)
print(make_PC_Plot())
dev.off()
}
)  

4) The myFilename function should only be returning a bare filename, without any directories; its job is to suggest to the browser what the user should save the file as.
Oh. I didn't get that from the manual - I somehow understood that it would be the location where the file is saved.
 
5) Your comment "hopefully this will catch the filename..." doesn't reflect what actually happens--but it shouldn't matter. FILE is just the filename of a temp file that Shiny created, it shouldn't matter where it is or what the filename is. Just put your data in there and let Shiny take care of the rest.
I will. I did. 

Now all that said, there is ALSO the bug that you are currently hitting, which was fixed in GitHub but not released on CRAN yet (the CRAN folks requested that people not update packages more than every 1-2 months). In order to get the fix you will need to run this code:

install.packages("devtools") # if you don't have devtools already
devtools::install_github("shiny", "rstudio")
I'll pass this on to my sysadmin. 

Again, thanks.

Luca

Joe Cheng

unread,
Feb 20, 2013, 12:19:15 PM2/20/13
to shiny-...@googlegroups.com
Great, glad it worked! Thanks Luca.

veep...@gmail.com

unread,
Mar 7, 2013, 3:26:32 AM3/7/13
to shiny-...@googlegroups.com, Luca Fenu, luca...@gmail.com
l
Hi Luca Fenu
Why this code is not giving the output?
I read your code but could not understand how to download pdf plot.
I need your help. 
with regards
veepsirtt

library(shiny)

# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {    
  
  output$distPlot <- renderPlot({
    
    ############### 
    #Save the plot 
    ###############
    jpeg("myplots.jpg")
    
    for(i in 1:10) {
      y <- rnorm(input$obs)
      #x <- rnorm(100)
      plot(y)
    }
    dev.off() 
    ##########################
    #Read the plot for Display
    ##########################
    library(EBImage)
    x = readImage('myplots.jpg')
    display(x)
    
   
  })
})

Winston Chang

unread,
Mar 8, 2013, 10:50:26 AM3/8/13
to shiny-...@googlegroups.com
At the moment, there's not a simple way to send already-saved image files to the client, but there will be a way to do it in the near future.
-Winston
Reply all
Reply to author
Forward
0 new messages