Save reactive plot to temp directory as png from shiny app - r

This question is quite basic and related to some questions before How to save reactive plot as png to working directory in a shiny app
I had to change my strategy creating a plot from a shiny app in Rmarkdown.
For this I need to accomplish this simple task:
How can I save this plot to the temp folder as png?
Background: After saving to temp folder I will transfer it to R markdown to create a report.
library(shiny)
ui <- basicPage(
plotOutput("plot1"),
)
server <- function(input, output) {
output$plot1 <- renderPlot({
plot(mtcars$wt, mtcars$mpg)
})
}
shinyApp(ui, server)
Update: My original code looks like this. I can't provide a reproducible example with this because it is to complex:
How can I implement the answer by ismirsehregal to this code:
# plot: Radarplot ------
output$radar <- renderChartJSRadar({
chartJSRadar(PSA_13()[,c(1,2,6)],
main = "XXX",
maxScale = 100, scaleStepWidth = 10, scaleStartValue = 0, labelSize = 12,
addDots = TRUE, showToolTipLabel = TRUE, showLegend = TRUE, lineAlpha = 0.8,
polyAlpha = 0.2, responsive = FALSE,
colMatrix = col2rgb(c("orange", "navy" ,"grey")))
})
# create markdown report with radar plot ----------------------------------
output$report <- downloadHandler(
filename = "report.pdf",
content = function(file) {
td <- tempdir()
tempReport <- file.path(td, "report.Rmd")
tempLogo <- file.path(td, "logo.png")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
file.copy("logo.png", tempLogo, overwrite = TRUE)
params <- list(scores = PSA_13()[,c(1,2,6)])
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
report.Rmd
---
geometry: margin=20truemm
fontfamily: mathpazo
fontsize: 11pt
documentclass: article
classoption: a4paper
urlcolor: blue
output:
pdf_document
header-includes:
- \usepackage{fancyhdr}
- \pagestyle{fancy}
- \rhead{\includegraphics[width = .05\textwidth]{logo.png}}
params:
scores: NA
plot_object: NA
---
\pagenumbering{gobble}
```{r setup, include=FALSE}
knitr::opts_chunk$set()
library(draw)
```
```{r rectangle, echo=FALSE}
drawBox(x =1.3, y = 3.7, width = 2.5, height = 1)
```
\vspace{-80truemm}
```{r plotout, echo=FALSE, message=FALSE, out.width='100%'}
params$plot_object
```
<!-- ```{r, echo=FALSE, out.width="100%", } -->
<!-- chartJSRadar(params$scores, width = 700, height = 700, -->
<!-- main = "Peritoneal Surface Calculator Radarchart", -->
<!-- maxScale = 100, -->
<!-- scaleStepWidth = 10, -->
<!-- scaleStartValue = 0, -->
<!-- labelSize = 14, -->
<!-- addDots = TRUE, -->
<!-- showToolTipLabel = FALSE, -->
<!-- showLegend = TRUE, -->
<!-- lineAlpha = 0.8, -->
<!-- polyAlpha = 0.2, -->
<!-- responsive = FALSE, -->
<!-- colMatrix = col2rgb(c("orange", "navy" ,"grey"))) -->
<!-- ``` -->
I feel quite near to the solution and I am very grateful for your time!

There is no need to save a temporary png file. We can use recordPlot instead:
library(shiny)
library(datasets)
writeLines(con = "report.Rmd", text = "---
title: 'Plot report'
output: html_document
params:
plot_object: NA
---
```{r plotout, echo=FALSE, message=FALSE, out.width='100%'}
params$plot_object
```")
ui = fluidPage(
plotOutput("plot1"),
downloadButton("report_button", "Generate report")
)
server = function(input, output, session) {
reactivePlot1 <- reactive({
plot(mtcars$wt, mtcars$mpg)
recordPlot()
})
output$plot1 <- renderPlot({
reactivePlot1()
})
output$report_button <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- tempfile(fileext = ".Rmd") # make sure to avoid conflicts with other shiny sessions if more params are used
file.copy("report.Rmd", tempReport, overwrite = TRUE)
rmarkdown::render(tempReport, output_format = "html_document", output_file = file, output_options = list(self_contained = TRUE),
params = list(plot_object = reactivePlot1())
)
}
)
}
shinyApp(ui, server)
Please see my related answer here.
After OPs update - using dummy data:
app.R:
library(shiny)
library(radarchart)
scores <- data.frame("Label"=c("Communicator", "Data Wangler", "Programmer",
"Technologist", "Modeller", "Visualizer"),
"Rich" = c(9, 7, 4, 5, 3, 7),
"Andy" = c(7, 6, 6, 2, 6, 9),
"Aimee" = c(6, 5, 8, 4, 7, 6))
ui = fluidPage(
chartJSRadarOutput("radar", width = "450", height = "300"),
downloadButton("report", "Generate report")
)
server = function(input, output, session) {
reactiveRadar <- reactive({
chartJSRadar(scores, maxScale = 10, showToolTipLabel=TRUE)
})
# plot: Radarplot ------
output$radar <- renderChartJSRadar({
reactiveRadar()
})
# create markdown report with radar plot ----------------------------------
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
td <- tempdir()
tempReport <- file.path(td, "report.Rmd")
# tempLogo <- file.path(td, "logo.png")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# file.copy("logo.png", tempLogo, overwrite = TRUE)
params <- list(scores = "Test", plot_object = reactiveRadar()) # scores = PSA_13()[,c(1,2,6)]
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
shinyApp(ui, server)
report.Rmd:
---
geometry: margin=20truemm
fontfamily: mathpazo
fontsize: 11pt
documentclass: article
classoption: a4paper
urlcolor: blue
output:
html_document
header-includes:
- \usepackage{fancyhdr}
- \pagestyle{fancy}
# - \rhead{\includegraphics[width = .05\textwidth]{logo.png}}
params:
scores: NA
plot_object: NA
---
\pagenumbering{gobble}
```{r setup, include=FALSE}
knitr::opts_chunk$set()
library(draw)
```
```{r rectangle, echo=FALSE}
drawBox(x =1.3, y = 3.7, width = 2.5, height = 1)
```
\vspace{-80truemm}
```{r plotout, echo=FALSE, message=FALSE, out.width='100%'}
params$plot_object
```

There are multiple options here.
In your case you could go with a simple option like this:
library(shiny)
ui <- basicPage(
plotOutput("plot1"),
actionButton("save", "Click to save")
)
server <- function(input, output) {
output$plot1 <- renderPlot({
plot(mtcars$wt, mtcars$mpg)
})
observeEvent("save", {
png('C:/path/to/your_plot/plot_saved.png')
plot(mtcars$wt, mtcars$mpg)
dev.off()
})
}
shinyApp(ui, server)
If you want to specify size, resolution, etc. you will have to customize the code within the observeEvent

Related

R Markdown cannot find object

I want to output Shiny app as a PDF report. But R Markdown gives the following error "Warning: Error in eval: object 'plot2' not found".
The plot works in Shiny, I'm not too familiar with Markdown and I'm getting really frustrated since I feel like I have tried quite a few potential solutions. What am I doing wrong?
Shiny UI
library(ggplot2)
library(tidyr)
library(DBI)
library(RODBCext)
library(shiny)
library(knitr)
library(kableExtra)
ui <- fluidPage(
pageWithSidebar(
headerPanel(""),
sidebarPanel(
textInput("Table", ""),
downloadButton("downloadData", "Download"),
submitButton(text="Submit"),
mainPanel(
tabsetPanel(
tabPanel("Data", tableOutput("tTable"))
),
tabsetPanel(
tabPanel("Graph", plotOutput("plot1")
)
)
)
Shiney Server
server <- function(input, output, session)
{
SQLData <- reactive({
#SQL Query
})
output$tTable <- renderTable(SQLData())
output$plot1 <- renderPlot({
da <- gather(SQLData(), key=Result, 'Control', 'Sample'
)
da2 <- data.frame(da)
ggplot(da2,aes(x=Result,y=Control, color=Result))+geom_point(size = 5) + scale_y_continuous(limits = c(80, 120)) +labs(y="", x = "")})
output$downloadData <- downloadHandler(
filename = "report.pdf",
content = function(file)
{ params <- list(table = SQLData(),
plot2 = {
da <- gather(SQLData(), key=Result, 'Control', 'Sample'
)
da2 <- data.frame(da)
ggplot(da2,aes(x=Result,y=Control, color=Result))+geom_point(size = 5) + scale_y_continuous(limits = c(80, 120)) +labs(y="", x = "")})}
)
rmarkdown::render(input = "Report.Rmd",
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
shinyApp(ui = ui, server = server)
R Markdown Report
---
title: "Report"
output: pdf_document
header-includes:
- \usepackage{float}
- \usepackage{booktabs}
- \usepackage{makecell}
params:
table: NA
plot2: NA
---
```{r echo = FALSE, eval = TRUE}
kable(params$table, format="latex", booktabs=TRUE) %>%
kable_styling(latex_options="scale_down")
```{r echo = FALSE, eval = TRUE}
library(ggplot2)
print(plot2)

R Shiny: Compiling RMarkdown Documents with Download Buttons in Data Table

I'm trying to make a reactive data table in R Shiny that has a button you can press to compile an RMarkdown document. Ultimately, I'm trying to combine the solutions from these two links:
R Shiny: Handle Action Buttons in Data Table and https://shiny.rstudio.com/articles/generating-reports.html. Here is what I have so far:
library(shiny)
library(shinyjs)
library(DT)
shinyApp(
ui <- fluidPage(
DT::dataTableOutput("data")
),
server <- function(input, output) {
useShinyjs()
shinyInput <- function(FUN, len, id, ...) {
inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))
}
inputs
}
df <- reactiveValues(data = data.frame(
Portfolio = c('Column1', 'Column2'),
Option_1 = shinyInput(downloadButton, 2, 'compile_', label = "Compile Document", onclick = 'Shiny.onInputChange(\"compile_document\", this.id)' ),
stringsAsFactors = FALSE,
row.names = 1:2
))
output$data <- DT::renderDataTable(
df$data, server = FALSE, escape = FALSE, selection = 'none', filter='top'
)
output$compile_document <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
Here is the RMarkdown document I'd like to compile:
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
```
The pieces all seem to be there, but I can't connect the "Compile Document" button to the download handler.
Here is a way that does not use downloadHandler.
library(shiny)
library(DT)
library(base64enc)
library(rmarkdown)
js <- '
Shiny.addCustomMessageHandler("download", function(b64){
const a = document.createElement("a");
document.body.append(a);
a.download = "report.docx";
a.href = b64;
a.click();
a.remove();
})
'
buttonHTML <- function(i){
as.character(
actionButton(
paste0("button_", i), label = "Report",
onclick = sprintf("Shiny.setInputValue('button', %d);", i)
)
)
}
dat <- data.frame(
PortFolio = c("Column 1", "Column 2")
)
dat$Action <- sapply(1:nrow(dat), buttonHTML)
ui <- fluidPage(
tags$head(tags$script(HTML(js))),
br(),
sliderInput("slider", "Sample size", min = 10, max = 50, value = 20),
br(),
DTOutput("dtable")
)
server <- function(input, output, session){
output[["dtable"]] <- renderDT({
datatable(dat, escape = -ncol(dat)-1)
})
observeEvent(input[["button"]], {
showNotification("Creating report...", type = "message")
tmpReport <- tempfile(fileext = ".Rmd")
file.copy("report.Rmd", tmpReport)
outfile <- file.path(tempdir(), "report.html")
render(tmpReport, output_file = outfile,
params = list(
data = dat[input[["button"]], -ncol(dat)],
n = input[["slider"]]
)
)
b64 <- dataURI(
file = outfile,
mime = "text/html"
)
session$sendCustomMessage("download", b64)
})
}
shinyApp(ui, server)
The rmd file:
---
title: "Dynamic report"
output: html_document
params:
data: "x"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
Row contents:
```{r}
params$data
```
A plot of `params$n` random points:
```{r}
plot(rnorm(params$n), rnorm(params$n))
```

Progress bar for kniting documents via shiny

I am trying to put a progress bar around my shiny downloadHandler(). The progress bar should show the render status of the rmarkdown HTML
I found this infomation on GitHub (https://github.com/rstudio/shiny/issues/1660) but could not get it to work. If I define no environment the file can not be knitted.
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report"),
textOutput("checkrender")
)
server <- function(input, output, session) {
output$checkrender <- renderText({
if (identical(rmarkdown::metadata$runtime, "shiny")) {
TRUE
} else {
FALSE
}
})
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider)
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
params$n
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
```
Your solution was quite close!
Two problems I see with your code:
You've left out the withProgress call in your downloadHandler code
The test for whether you're running in a shiny environment, if (identical(rmarkdown::metadata$runtime, "shiny")), needs to go in your .Rmd file. You enclose any calls to increment/set the progress bar in this test, otherwise the .Rmd code will produce errors like Error in shiny::setProgress(0.5) : 'session' is not a ShinySession object.
The below reworking of your code should work:
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report"),
textOutput("checkrender")
)
server <- function(input, output, session) {
output$checkrender <- renderText({
if (identical(rmarkdown::metadata$runtime, "shiny")) {
TRUE
} else {
FALSE
}
})
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
withProgress(message = 'Rendering, please wait!', {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider)
rmarkdown::render(
tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
})
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
params$n
if (identical(rmarkdown::metadata$runtime, "shiny"))
shiny::setProgress(0.5) # set progress to 50%
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
if (identical(rmarkdown::metadata$runtime, "shiny"))
shiny::setProgress(1) # set progress to 100%
```
Another version of answer.
With rmarkdown version 1.14, answer by jsavn seems to not work. Because
rmarkdown::metadata does not have $runtime. (I tried to capture value of rmarkdown::metadata$runtime by saving it as .rds during rendering by rmarkdown::render but it only had value of YAML and metadata$runtime was NULL.
So, for allow setProgress to work with "non-shiny" rendering, pass parameter from shiny-app may be better solution, since this will not depend on values of metadata (which may change as rmarkdown version changes).
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
)
server <- function(input, output, session) {
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
withProgress(message = 'Rendering, please wait!', {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider,
rendered_by_shiny = TRUE)
rmarkdown::render(
tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
})
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd
---
title: "Dynamic report"
output: html_document
params:
n: 10
rendered_by_shiny: FALSE
---
```{r}
params$n
if (params$rendered_by_shiny)
shiny::setProgress(0.5) # set progress to 50%
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
if (params$rendered_by_shiny)
shiny::setProgress(1) # set progress to 100%
```

Can't generate R-Markdown report within ShinyApp

I've created ShinyApp where everything works fine. I'd like to add downloadHandler to generate Markdown report that contains the chosen plot. Firstly, I upload a file into ShinyApp. Nextly, I select variables to be plotted using checkBoxInput. Next step is using dropdown list to select between Lattice/ggplot2 plot and finally I'd like to click download it and get it.
Unfortunately, every time I do try to download it I receive a blank Markdown page. It doesn't really matter what format of report will be generated. I'd like to get an appropiate logic for this task. I tried both solutions I found in a network:
output$downloadReport <- downloadHandler(
filename = function() {
paste('my-report', sep = '.', switch(
input$format, PDF = 'pdf', HTML = 'html', Word = 'docx')
)
},
content = function(file) {
src <- normalizePath('report.Rmd')
owd <- setwd(getwd())
on.exit(setwd(owd))
file.copy(src, 'report.Rmd', overwrite = TRUE)
out <- render('report.Rmd', switch(
input$format,
PDF = pdf_document(), HTML = html_document(), Word = word_document()
))
file.rename(out, file)
})
and
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(getwd(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(graph = input$graph, colsel = input$colsel)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
So respectively I created report.rmd templates for my app to fullfill it. I tried to put a lot of things inside but none of these works. Do I miss the logic for the template?
---
title: "Untitled"
author: "user"
date: "date"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r plot, echo=TRUE}
plotdata <- reactive({
d <- dataIn()[, c(req(input$colsel))]
d <- melt(d, id.vars="Numer")
})
plot1 <- reactive({
dotplot(value~Numer, data=plotdata(), auto.key = list(space="right", title="Types"), groups=variable)
})
plot2 <- reactive({
ggplot(plotdata(), aes(x=Numer, y=value, color=variable)) +
geom_point()
})
graphInput <- reactive({
switch(input$graph,
"Lattice" = plot1(),
"ggplot2" = plot2())
})
renderPlot({
graphInput()
})
})
```
Alright, I got it finally! Firstly, we need to run shinyApp using function "Run External". Secondly we don't need that mess I made in the template. Simple:
---
title: "Untitled"
author: "user"
date: "date"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(shiny)
library(ggplot2)
library(lattice)
library(markdown)
```
```{r plot}
plot1()
plot2()
graphInput()
```
Where plot1(), plot2() and graphinput() represent my:
plot1 <- reactive({
dotplot(value~Numer,data=plotdata(), auto.key = list(space="right", title="WWW"), groups=variable)
})
plot2 <- reactive({
ggplot(plotdata(), aes(x=Numer, y=value, color=variable)) +
geom_point()
})
graphInput <- reactive({
switch(input$graph,
"Lattice" = plot1(),
"ggplot2" = plot2()
)
})

Side by Side plotly plot in rmarkdown pdf

I have a big shiny app and I'm making a downloadable pdf with rmarkdownfrom the content in it. The problem I'm having is that all the plots are in plotlyand I haven't found how to plot 2 plots in the same row of the pdf file, in R it would be a simple subplot but it doesn't work.
This is a toy example of what I have:
shinyApp(
ui = fluidPage(
downloadButton("reporte", "Generate report"),
plotlyOutput("plotTest"),
plotlyOutput("plotHist")
),
server = function(input, output) {
library(webshot)
data = as.data.frame(rnorm(1000))
plotTest = plot_ly(y = ~rnorm(1000),type = "scatter",mode = "lines")
plotHist = plot_ly(x = ~rnorm(1000),type = "histogram")
output$plotTest = renderPlotly({plotTest})
output$plotHist = renderPlotly({plotHist})
output$reporte <- downloadHandler(
filename = "reporte.pdf",
content = function(file) {
tempReport <- file.path("C:/Users/Alejandro/Documents/test", "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n=plotTest,k=plotHist)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
report.Rmd:
---
title: "Ensayo Reporte"
output: pdf_document
always_allow_html: yes
params:
n: NA
k: NA
---
```{r,echo=FALSE}
library(plotly)
tmpFile <- tempfile(fileext = ".png")
export(params$n, file = tmpFile)
export(params$k, file = tmpFile)
```
Adding the ususal fig.align='center',fig.show='hold' doesn't work i'll just get: Warning: Error in : pandoc document conversion failed with error 43

Resources