R + Shiny: qr code generation and download - r

I want to create an app where the user can input a link or some text and download the corresponding QR code as a pdf.
I have already the fundamental building blocks, but I cannot glue them together.
For instance, for the pure QR code generation part
library(qrcode)
qr <- qr_code("https://www.wikipedia.org/")
pdf("qr_code.pdf")
plot(qr)
dev.off()
#> png
#> 2
Created on 2022-01-04 by the reprex package (v2.0.1)
for inputting text in Shiny
library(shiny)
ui <- fluidPage(
textInput("caption", "Caption", "Your link/text here"),
verbatimTextOutput("value")
)
server <- function(input, output) {
output$value <- renderText({ input$caption })
}
shinyApp(ui, server)
#> PhantomJS not found. You can install it with webshot::install_phantomjs(). If it is installed, please make sure the phantomjs executable can be found via the PATH variable.
Shiny applications not supported in static R Markdown documents
Created on 2022-01-04 by the reprex package (v2.0.1)
and for saving a plot as a pdf in Shiny
library(shiny)
library(tidyverse)
df <- tibble(x=seq(10), y=seq(10))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
downloadButton("save", "Download plot"),
),
mainPanel(
plotOutput("tplot" )
)
)
)
server <- function(input, output) {
tplot <- reactive({
plot(df$x, df$y)
})
output$tplot <- renderPlot({
tplot()
})
# downloadHandler contains 2 arguments as functions, namely filename, content
output$save <- downloadHandler(
filename = function() {
paste("myplot.pdf")
},
# content is a function with argument file. content writes the plot to the device
content = function(file) {
pdf(file) # open the pdf device
plot(x=df$x, y=df$y) # draw the plot
dev.off() # turn the device off
}
)
}
shinyApp(ui = ui, server = server)
#> PhantomJS not found. You can install it with webshot::install_phantomjs(). If it is installed, please make sure the phantomjs executable can be found via the PATH variable.
Shiny applications not supported in static R Markdown documents
Created on 2022-01-04 by the reprex package (v2.0.1)
Can anyone help me put all of this together?
Thanks!

Here's how you can do this:
UI:
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
textInput("link", "Enter Link here", "www.google.com"),
downloadButton("save", "Download QR")
),
mainPanel(
plotOutput("tplot" )
)
)
)
textInput takes arguments inputId, label, and value.
inputId is what you'll refer to the input inside your code.
label tells what will be written over the input field. It is something that user can see and identify what to enter in the field.
'value` is the default value that your input field will have. It can be blank.
Server:
server <- function(input, output) {
tplot <- reactive({
qr <- qr_code(input$link)
plot(qr)
})
output$tplot <- renderPlot({
tplot()
})
# downloadHandler contains 2 arguments as functions, namely filename, content
output$save <- downloadHandler(
filename = function() {
paste("myplot.pdf")
},
# content is a function with argument file. content writes the plot to the device
content = function(file) {
pdf(file) # open the pdf device
plot(qr_code(input$link)) # draw the plot
dev.off() # turn the device off
}
)
}
Notice that I've used qr_code inside the reactive field so that you can use it further in output.
The shiny app will now, show the QR code as you keep typing inside the input field. Since it is reactive, it reacts to your input.
The download functionality also works as expected.

Related

R Shiny how to show status messages from the console (pdf_ocr_text)

When I use pdf_ocr_text from pdftools for example:text1 <- pdf_ocr_text("0.pdf", dpi = 300), it will show the status in the R console like below.
Converting page 1 to 0_1.png... done!
Converting page 2 to 0_2.png... done!
Converting page 3 to 0_3.png... done!
Converting page 4 to 0_4.png... done!
But how can I show this status when I use this in Shiny app? Because I want the user to see it's being processed rather than nothing is showing when they click the button (it can take a while for this to finish)?
Reproducible codes below, you can import any pdf files in there, but you will need to create a folder that's called www which should be in the same folder of your R file. Also run the app in external browser, otherwise don't work well.
library(tidyverse)
library(shiny)
library(pdftools)
library(tesseract)
library(tidytext)
library(reactable)
library(shinyFeedback)
library(shinyjs)
library(shinyalert)
ui <- shinyUI(fluidPage(
useShinyjs(),
useShinyalert(),
shinyFeedback::useShinyFeedback(),
sidebarLayout(
sidebarPanel(
titlePanel("Demo"),
fileInput("file_import", "Upload Files ( . pdf format only)",
multiple = T, accept = ".pdf"),
disabled(actionButton("ocr_button","OCR (click this when nothing shows up)",
class = "btn-danger",
icon=icon("fa-sharp fa-solid fa-triangle-exclamation",
lib = "font-awesome"))),
textOutput("sometext"),
tableOutput("files")
),
mainPanel(
uiOutput("pdfview"),
reactableOutput("test")
)
)
))
server <- function(input, output, session) {
### display the pdf ########################################################
x = reactiveVal(1)
observeEvent(input$file_import,{
enable("ocr_button")
file.rename(input$file_import$datapath[x()], "0.pdf")
file.copy("0.pdf","www", overwrite = T)
output$pdfview <- renderUI({
tags$iframe(style="height:1200px; width:100%", src="0.pdf")
})
})
observeEvent(input$ocr_button, {
### OCR ###########################################################
text1 <- reactive({pdf_ocr_text("0.pdf", dpi = 300)})
######################################################################
output$sometext = renderText({
text1()
})
})
}
shinyApp(ui, server)

Temporary memory error in Shiny with pdf viewer (UiOutput) and selectableTableOutput (shinyjqui package)

I have a super strange problem that I can't seem to figure out - I suspect it has something to do with writing/reading from temp directory. So what I'm trying to do is a long story, but i've reduced the problem down to two simple tasks.
View an uploaded pdf (with UiOutput)
Use selectable table (from shinyjqui package)
I have no problem doing these seperately, however, if I try combine them into one shiny app it always results in a crash. Here's my code:
library(tidyverse)
library(shiny)
library(shinyjqui)
ui <- fluidPage(
fluidRow(
column(6,
# Input pdf
fileInput("file1", "File"),
# Display an editable table (from shinyjqui)
selectableTableOutput("tbl", selection_mode = "cell")
),
column(6,
# display uploaded pdf
uiOutput("pdfview")
)
)
)
server <- function(input, output, session) {
# ---- pdf (1)
addResourcePath("pdf", tempdir())
test_file <- reactive({
req(input$file1$datapath)
readBin(con=input$file1$datapath,what = 'raw',n=input$file1$size)
})
observe({
temp <- paste0(resourcePaths(), "/doc.pdf")
writeBin(test_file(), temp)
output$pdfview <- renderUI({
tags$iframe(style="height:600px; width:100%", src="pdf/doc.pdf")
})
})
# ---- pdf (1)
# --- selectableTable (2)
get_dummy_data <- reactive({
# random dataframe
data.frame(col1 = c(1,2,3),col2 = c(1,2,3),col3 = c(1,2,3))
})
output$tbl <- renderTable(get_dummy_data(), rownames = TRUE)
# --- selectableTable (2)
}
shinyApp(ui, server)
Note that, if you run the above the selectable table will work, however, the moment you upload a pdf it crashes. Moreover, if you comment out chunk 2 (selectable table), the pdf viewer works (specifically used in browser - not shiny popup). Something funky is happening in memory, because if you run the app without chunk 2, the pdf viewer works, and then if you bring chunk 2 into the picture it crashes - but if you take away chunk 2 again and rerun it, it doesn't work at all (and it was in the same state as what it was when it worked originally), and I have to close and reopen the project for the pdf viewer to work. Here is the error:
Warning: Error in file: invalid 'description' argument
46: file
45: writeBin
44: <observer> [C:/Users/Chroo/OneDrive//Clients//Sandbox_0080921/test_pdf_viewer.R#34]
1: runApp
Any ideas what's happening?
Ok so after some browsing I found a solution from #amrrs on https://www.py4u.net/discuss/1545522. The issue is related to binary reading and writing, hence the strange memory behavior. The issue can be circumvented by copying the input file instead, because then the conflict with shinyjqui is avoided.
server <- shinyServer(function(input, output) {
observe({
req(input$file1)
#Remove
#test_file <- readBin(input$file1$datapath, what="raw")
#writeBin(test_file, "myreport.pdf")
#cat(input$file1$datapath)
# Replace
file.copy(input$file1$datapath,"www", overwrite = T)
output$pdfview <- renderUI({
tags$iframe(style="height:600px; width:100%", src="0.pdf")
})
})
})

Highcharter Shiny bulk command-line rendering and export

I've built an extensive Shiny app which relies heavily on Highcharter for its graphics. Now I'm trying to make a download button that allows the user to download a ZIP file with multiple PDF files, each having a different chart.
I managed to get it to work with ggPlot (hat-tip to Shiny R Zip multiple PDFS for download), but cannot seem to figure it out with Highcharts. I know part of the problem is because Highcharts doesn't output an image, but rather an SVG(? or perhaps JSON?). So I'm stuck on how to convert, within R/Shiny code, the chart to a PDF, since it's not rendered or displayed in the browser.
I tried first to render as SVG, then use rsvg_pdf() (from library rsvg) to convert to PDF, but to render as SVG we need to open a rendering device and close it with dev.off(), which saves the file. I couldn't find a way to capture this in-between and then pass it to rsvg_pdf().
Instead, would the solution lie in invoking the (unix) command-line, and do conversion there? Is there a way to pass a chart to the Highcharts exporting module without it showing up for the end-user?
Thanks a lot for your help!
See below for a reproducible example:
ui.R
library(shiny)
shinyUI(
fluidPage(
headerPanel(title="testing Downloadhandler"),
sidebarLayout(
sidebarPanel(
selectInput("ngear", "Select the gear number", c("Cylinders" = "cyl", "Transmission" = "am", "Gears" = "gear")),
submitButton("Update!"),br(),
),
mainPanel(
tabsetPanel(type="tab",
tabPanel("Plot", plotOutput("plot"),
downloadButton("downloadZippedPlots", "Download Zipped Plot")
)
)
)
)
)
)
Server.R
library(shiny)
library(highcharter)
shinyServer(function(input,output){
mtreact <- reactive({
mtcars[,c("mpg", input$ngear)]
})
output$plot <- renderPlot({
boxplot(mtreact())
})
output$downloadZippedPlots <- downloadHandler(
filename = function(){
paste("mtcars-plots-zipped", "zip", sep=".")
},
content = function(fname){
fs <- c()
tmpdir <- tempdir()
setwd(tempdir())
for (column in c("cyl", "am", "gear")) {
path <- paste(column, "pdf", sep=".")
fs <- c(fs, path)
# --- This works ---
pdf(paste(column, "pdf", sep="."))
boxplot(mtcars[,c("mpg", column)])
dev.off()
# --- This doesn't work ---
hchart(mtcars, "bar", hcaes(x=mpg, y=!!column))
# --- This also doesn't work ---
this_chart <- hchart(mtcars, "bar", hcaes(x=mpg, y=!!column))
print(this_chart)
dev.off()
}
zip(zipfile=fname, files=fs)
},
contentType = "zip"
)
})

Server permissions for shiny downloadhandeller()

I am trying to setup a shiny app that can download html plots from the googleViz package. The code works on my machine, but when I move it to the server I get the following message when testing the download...
"The requested URL was rejected. Please consult with your administrator."
I am struggling to figure out what the IT staff, that set up the server, need to do to fix the problem - I know nothing about servers and they know nothing about R.
I built a small example app here to demonstrate the problem, based on the following ui.R
library(shiny)
library(googleVis)
# user interface
shinyUI(pageWithSidebar(
headerPanel("googleVis on Shiny"),
sidebarPanel(
selectInput("dataset", label = "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
downloadButton('download_gvis', label = 'Download')
),
mainPanel(
htmlOutput("view")
)
))
and server.R
library(googleVis)
library(webshot)
shinyServer(function(input, output) {
# data set from user
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# plot of data set from user
my_plot <- reactive({
gvisScatterChart(datasetInput(),
options=list(title=paste('Data:',input$dataset)))
})
# render plot of data set from user
output$view <- renderGvis({
my_plot()
})
# download plot of data set from user
output$download_gvis <- downloadHandler(
filename = "test.png",
content = function(file) {
g <- my_plot()
# print to html file
print(g, file = "gg.html")
# take a webshot of html file and save as png
webshot(
url = "gg.html",
file = "output.png",
delay = 2
)
# send output file to downloadHandler
file.copy("output.png", file)
# delete files
file.remove("gg.html")
file.remove("output.png")
}
)
})
I think the code breaks at print(g, file = "gg.html") in the server script. Thegg.html file never appears in the server directory (on my local machine I see it pop up in the directory view of RStudio).

Calling the filename from a reactive dataset in shiny r

I am currently writing a shiny app which imports a dataset and displays a manipulated version. To work on the shiny methods I am currently working on a simplified version which displays the imported dataset. I currently assign the imported dataset to a reactive value, and then use the render table as follows:-
shinyServer(function(input, output) {
DATA<-reactive({
input$filein
})
output$Dataset <- renderTable({
DATA()
})
})
The interface then produces a table with the following columns:-
name, size, type, datapath.
What I had in mind was to call the datapath variable, and use read.csv to call it within the renderTable function. I tried using:-
DATA()$datapath
However that doesn't seem to produce any result. Are there any other ways to extract this data within Shiny? I contemplated using vector indices as you would using regular R code however I am unsure as to whether or not that'll work within Shiny.
Here is an example for files in the current working directory. The example file I used was a minimal csv file (see bottom). Please note however that this is indeed limited to files in your working directory. If you want other files to be loaded you will need to have a further component to specify the path (possibly in the selectInput).
library(shiny)
library(tools)
runApp(
list(
ui = pageWithSidebar(
headerPanel("File Info Test"),
sidebarPanel(
p("Demo Page."),
selectInput("filein", "Choose File", choices=c("test.csv"))
),
mainPanel(
tableOutput("myTableInfo"),
tableOutput("myTable")
)
),
server = function(input, output){
mydata <- reactive({
read.csv(input$filein)
})
file_info <- reactive({
validate(
need(!is.null(input$filein), "please select file"
)
)
name <- input$filein
size <- file.info(input$filein)[['size']]
type <- file_ext(input$filein)
datapath <- file_path_as_absolute(input$filein)
cbind(name, size, type, datapath)
})
output$myTableInfo <- renderTable({
file_info()
})
output$myTable <- renderTable({
mydata()
})
}
)
)
test.csv
X1,X2,X3
1,2,3
4,5,6

Resources