Downloading graphs in Shiny - r

I am trying to download the graph I made in Shiny, I found a Stackoverflow post about this subject here. However, when I run the code from the answer, all seeems to work fine, exept that I can't open the graphs once they are "saved". I can't see them in the folder I saved them in and when I try to open them from my recent files, an error "file not found" pops up.
This is the code I'm using:
library(shiny)
library(ggplot2)
runApp(list(
#ui
ui = fluidPage(downloadButton('downloadPlot')),
#server
server = function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
plotInput <- reactive({
df <- datasetInput()
p <-ggplot(df, aes_string(x=names(df)[1], y=names(df)[2])) +
geom_point()
})
output$downloadPlot <- downloadHandler(
filename = function() { paste(input$dataset, '.png', sep='') },
content = function(file) {
ggsave(file, plot = plotInput(), device = "png")
}
)
}
))

Alternatively, you can use plotly which supplies a download possibility without further configurations (download button is in the upper right corner of the graph):
library(shiny)
library(plotly)
runApp(list(
#ui
ui = fluidPage(selectInput("dataset", "Choose a dataset:", choices = c("rock", "pressure", "cars")),
plotlyOutput('plot')),
#server
server = function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$plot <- renderPlotly({
df <- datasetInput()
ggplot(df, aes_string(x=names(df)[1], y=names(df)[2])) +
geom_point()
ggplotly()
})
}
))

I tried to replicate your code with textInput and this works fine for me.
library(shiny)
library(ggplot2)
runApp(list(
#ui
ui = fluidPage(downloadButton('downloadPlot'),
textInput("filename", "Choose a dataset:")),
#server
server = function(input, output) {
datasetInput <- reactive({
switch(input$filename,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
plotInput <- reactive({
df <- datasetInput()
p <- ggplot(df, aes_string(x=names(df)[1], y=names(df)[2])) +
geom_point()
})
output$downloadPlot <- downloadHandler(
filename = function() { paste(input$filename, '.png', sep='') },
content = function(file) {
ggsave(file, plot = plotInput(), device = "png")
}
)
}
))

Related

ShinyApp: How to pass plot from one module into another one in order to download it in a report?

I want to generate a report that include a plot from another module, but my code does not work.
I have checked few examples from other shiny developers, but my code does not work.
As far as I understand, I need to:
(1) return the plot the plot module
(2) pass it to the download server
(3) also need to include it in the main server (I don't understand the logic here, I need your help for explanation)
My minimal example:
# Module 1,
plot_ui <- function(id) {
ns <- NS(id)
tagList(
plotOutput(ns("plot"))
)
}
plot_server <- function(id) {
moduleServer(id, function(input, output, session) {
myplot <- reactive({plot(x = mtcars$wt, y = mtcars$mpg)})
output$plot <-renderPlot({
myplot()
})
return(myplot)
})
}
# Module 2
download_ui <- function(id) {
ns <- NS(id)
tagList(
downloadButton(outputId = ns("report_button"),
label = "Generate report"
)
)
}
download_server <- function(id, myplot) {
moduleServer(id, function(input, output, session){
output$report_button<- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "myRport.Rmd")
file.copy("myReport.Rmd", tempReport, overwrite = TRUE)
params <- list(plot1 = myplot())
rmarkdown::render(tempReport,
output_file = file,
params = params
)
}
)
}
)
}
# Application
library(shiny)
app_ui <- function() {
fluidPage(
plot_ui("plot_ui_1"),
download_ui("download_ui_2")
)
}
app_server <- function(input, output, session) {
getPlot <- plot_server("plot_ui_1")
download_server("download_ui_2", myplot = getPlot)
}
shinyApp(app_ui, app_server)
My markdown file
---
title: "Test"
output: html_document
params:
plot1: NA
---
```{r}
params$plot1
Basically you have done everything right. However, to make you code work you have to render your Rmd in a new environment by adding envir = new.env(parent = globalenv()) to rmarkdown::render. See e.g. Generating downloadable reports. Moreover TBMK you can't pass a base R plot to an Rmd via params, i.e. while your code works no plot will be displayed in the rendered report. That's why I switched to ggplot2:
# Module 1,
plot_ui <- function(id) {
ns <- NS(id)
tagList(
plotOutput(ns("plot"))
)
}
plot_server <- function(id) {
moduleServer(id, function(input, output, session) {
myplot <- reactive({
#plot(x = mtcars$wt, y = mtcars$mpg)
ggplot(mtcars, aes(wt, mpg)) +
geom_point()
})
output$plot <-renderPlot({
myplot()
})
return(myplot)
})
}
# Module 2
download_ui <- function(id) {
ns <- NS(id)
tagList(
downloadButton(outputId = ns("report_button"),
label = "Generate report"
)
)
}
download_server <- function(id, myplot) {
moduleServer(id, function(input, output, session){
output$report_button<- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "myRport.Rmd")
file.copy("myReport.Rmd", tempReport, overwrite = TRUE)
params <- list(plot1 = myplot())
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
}
# Application
library(shiny)
library(ggplot2)
app_ui <- function() {
fluidPage(
plot_ui("plot_ui_1"),
download_ui("download_ui_2")
)
}
app_server <- function(input, output, session) {
getPlot <- plot_server("plot_ui_1")
download_server("download_ui_2", myplot = getPlot)
}
shinyApp(app_ui, app_server)

Shiny: how to export multiple outputs (plots and tables) from one module and pass them to a report?

I have asked a similar question to pass one output from a module to another module to generate a report.
However, I still cannot figure out how to export multiple outputs to include them in a downloadable report.
What I did is first to save the multiple outputs in a list, and then export them.
But I don't known how to pass them to the download module. In specific, I don't understand what does plotList = getPlot mean in download_server("download_ui_2", plotList = getPlot). I thought this is used to pass the multiple output from module getPlot to module download. But it did not work. I got the following error:
Error in download_server("download_ui_2", plotList = getPlot) :
unused argument (plotList = getPlot)
Could someone help me out and explain me a bit of the above code?
Thanks a lot!
library(shiny)
library(ggplot2)
library(rmarkdown)
# Module 1,
plot_ui <- function(id) {
ns <- NS(id)
tagList(
plotOutput(ns("plot1")),
plotOutput(ns("plot2"))
)
}
plot_server <- function(id) {
moduleServer(id, function(input, output, session) {
myplot1 <- reactive({
ggplot(mtcars, aes(wt, mpg)) +
geom_point()
})
myplot2 <- reactive({
ggplot(mtcars, aes(wt, drat)) +
geom_point()
})
output$plot1 <-renderPlot({
myplot1()
})
output$plot2 <-renderPlot({
myplot2()
})
plotList <- list(myplot1 = myplot1, myplot2 = myplot2) ## save in a list
return(plotList)
})
}
# Module 2
download_ui <- function(id) {
ns <- NS(id)
tagList(
downloadButton(outputId = ns("report_button"),
label = "Generate report"
)
)
}
download_server <- function(id, myplot) {
moduleServer(id, function(input, output, session){
output$report_button<- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "myRport.Rmd")
file.copy("myReport.Rmd", tempReport, overwrite = TRUE)
params <- list(plot1 = myplot1())
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
}
# Application
library(shiny)
library(ggplot2)
app_ui <- function() {
fluidPage(
plot_ui("plot_ui_1"),
download_ui("download_ui_2")
)
}
app_server <- function(input, output, session) {
getPlot <- plot_server("plot_ui_1")
download_server("download_ui_2", plotList = getPlot)
}
shinyApp(app_ui, app_server)
The code works now after help from #Limey.
library(shiny)
library(ggplot2)
library(rmarkdown)
# Module 1,
plot_ui <- function(id) {
ns <- NS(id)
tagList(
plotOutput(ns("plot1")),
plotOutput(ns("plot2"))
)
}
plot_server <- function(id) {
moduleServer(id, function(input, output, session) {
myplot1 <- reactive({
ggplot(mtcars, aes(wt, mpg)) +
geom_point()
})
myplot2 <- reactive({
ggplot(mtcars, aes(wt, drat)) +
geom_point()
})
output$plot1 <-renderPlot({
myplot1()
})
output$plot2 <-renderPlot({
myplot2()
})
plotList <- list(myplot1 = myplot1, myplot2 = myplot2)
return(plotList)
})
}
# Module 2
download_ui <- function(id) {
ns <- NS(id)
tagList(
downloadButton(outputId = ns("report_button"),
label = "Generate report"
)
)
}
download_server <- function(id, plotList) {
moduleServer(id, function(input, output, session){
output$report_button<- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "myRport.Rmd")
file.copy("myReport.Rmd", tempReport, overwrite = TRUE)
params <- list(plot1 = plotList$myplot1(),
plot2 = plotList$myplot2()
)
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
}
# Application
library(shiny)
library(ggplot2)
app_ui <- function() {
fluidPage(
plot_ui("plot_ui_1"),
download_ui("download_ui_2")
)
}
app_server <- function(input, output, session) {
getPlot <- plot_server("plot_ui_1")
download_server("download_ui_2", plotList = getPlot)
}
shinyApp(app_ui, app_server)

How to render a plot from a list of multiple objects on ShinyApp

I have a function Identify_IP() that returns a list of 1- dataframe
2-ggplot. I need to renderTable and renderPlot in ShinyApp. This shinyApp code renders only the dataframe. But I can't render the plot. Any help?
library(shiny)
source('InflectionP2.R', local = TRUE)
runApp(
list(
ui = fluidPage(
titlePanel("Upload your file"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xls file',
accept = c(".XLS")),
actionButton("btn", "Update Table"),
actionButton("btn1", "Display Plot"),
downloadButton('downloadData', 'Download')
),
mainPanel(
tableOutput('what'),
plotOutput('pl'))
)
)
,
server = function(input, output, session){
dataOP <- reactive({
inFile <- input$file1
if (is.null(input$file1))
return(NULL)
Identify_IP(read.table(inFile$datapath))
list(tble = df1, txt = inflection_points, plt = p )
})
observeEvent(input$btn, output$what <- renderTable({dataOP()$tble}))
observeEvent(input$btn1, output$pl <- renderPlot({
plot(dataOP()$plt)
}))
}
))
Using the following server worked for me:
server = function(input, output, session){
dataOP <- reactive({
inFile <- input$file1
if(is.null(input$file1)){
return(NULL)
}
#Identify_IP(read.table(inFile$datapath))
list(tble = c(1:20), txt = c(1:10), plt = rnorm(100))
})
observeEvent(input$btn,{
output$what <- renderTable({
dataOP()$tble
})
})
observeEvent(input$btn1,{
output$pl <- renderPlot({
plot(dataOP()$plt)
})
})
}
Note that I commented out your function Identify_IP and replaced the results with arbitrary values.
If this still doesn't work your problem probably is probably related to this function or with the values returned by the function, respectively.

Saving PNG plot non-ggplot in Shiny

I'm trying to download a non-ggplot file in Shiny. I can see the plot in the app, but when I click the plotDownload button in UI, it download an EMPTY png file. Someone can have idea what I'm doing wrong??
server.R
library(shiny)
shinyServer(
function(input, output) {
plotInput <- reactive({
plot(rnorm(sample(100:1000,1)))
})
output$pngPlot <- renderPlot({ plotInput() })
output$downloadPlot <- downloadHandler(
filename = "myPlot.png",
content = function(file){
png(file, width=800, res=100)
print(plotInput())
dev.off()
})
}
)
ui.R
require(shiny)
pageWithSidebar(
headerPanel("Output to png"),
sidebarPanel(
downloadButton('downloadPlot')
),
mainPanel({ mainPanel(plotOutput("pngPlot")) })
)
Thnks.
You need to replot within the downloadHandler():
library(shiny)
shinyServer(
function(input, output) {
plotInput <- reactive({
plot(rnorm(sample(100:1000,1)))
})
output$pngPlot <- renderPlot({ plotInput() })
output$downloadPlot <- downloadHandler(
filename = "myPlot.png",
content = function(file){
png(file, width=800, res=100)
plot(rnorm(sample(100:1000,1)))
dev.off()
})
}
)

Outputting Shiny (non-ggplot) plot to PDF

Is there a method to output (UI end) Shiny plots to PDF for the app user to download? I've tried various methods similar to those involving ggplot, but it seems downloadHandler can't operate in this way. For example the following just produces broken PDF's that don't open.
library(shiny)
runApp(list(
ui = fluidPage(downloadButton('foo')),
server = function(input, output) {
plotInput = reactive({
plot(1:10)
})
output$foo = downloadHandler(
filename = 'test.pdf',
content = function(file) {
plotInput()
dev.copy2pdf(file = file, width=12, height=8, out.type="pdf")
})
}
))
Very grateful for assistance.
Solved. The plot should be saved locally with pdf(), not the screen device (as with dev.copy2pdf). Here's a working example: shiny::runGist('d8d4a14542c0b9d32786'). For a nice basic model try:
server.R
library(shiny)
shinyServer(
function(input, output) {
plotInput <- reactive({
if(input$returnpdf){
pdf("plot.pdf", width=as.numeric(input$w), height=as.numeric(input$h))
plot(rnorm(sample(100:1000,1)))
dev.off()
}
plot(rnorm(sample(100:1000,1)))
})
output$myplot <- renderPlot({ plotInput() })
output$pdflink <- downloadHandler(
filename <- "myplot.pdf",
content <- function(file) {
file.copy("plot.pdf", file)
}
)
}
)
ui.R
require(shiny)
pageWithSidebar(
headerPanel("Output to PDF"),
sidebarPanel(
checkboxInput('returnpdf', 'output pdf?', FALSE),
conditionalPanel(
condition = "input.returnpdf == true",
strong("PDF size (inches):"),
sliderInput(inputId="w", label = "width:", min=3, max=20, value=8, width=100, ticks=F),
sliderInput(inputId="h", label = "height:", min=3, max=20, value=6, width=100, ticks=F),
br(),
downloadLink('pdflink')
)
),
mainPanel({ mainPanel(plotOutput("myplot")) })
)
(Hello), just use pdf :
library(shiny)
runApp(list(
ui = fluidPage(downloadButton('foo')),
server = function(input, output) {
plotInput = reactive({
plot(1:10)
})
output$foo = downloadHandler(
filename = 'test.pdf',
content = function(file) {
pdf(file = file, width=12, height=8)
plotInput()
dev.off()
})
}
))
EDIT : I don't know... It's weird. A workaround is to use dev.copy2pdf like you did in the first place but in the reactive function instead downloadHandler :
## server.R
library(shiny)
shinyServer(
function(input, output) {
plotInput <- reactive({plot(rnorm(1000))
dev.copy2pdf(file = "plot.pdf")
})
output$myplot <- renderPlot({ plotInput() })
output$foo <- downloadHandler(
filename <- "plot.pdf",
content <- function(file) {
file.copy("plot.pdf", file)
})
}
)

Resources