Display locally-stored image in R Shiny - r

I spent a fair amount of time trying to solve that issue.
Of course I did my homework before sharing my issue here.
In particular I have unsuccessfully consulted :
local image in shiny app without img(src())?
Shiny can not display Image locally
adding local image with html to a Shiny app
R Shiny img() on UI side does not render the image
Display images from web in shiny R
Image failing to display in R shiny
Embedding Image in Shiny App
How to place an image in an R Shiny title
So I did create a 'www' folder at the root of the RStudio project file where I put some pictures.
These pictures are used in the titlePanel but also by the main htmlwidget the application calls.
It is crucial for me to have these pictures stored locally because the application may be running in a secured environment without any access to the Internet.
I tried a relative path to these pictures and an absolute path: no picture was displayed.
Then I noticed some kind of inconsistency: I experience this issue only when I run the application through the regular command in RStudio, "Run Selected Line(s)".
On the other hand, when I run the application through the dedicated command "Run App" (in the top right corner in RStudio, green arrow), I don't have this issue anymore, the pictures display nicely (but the input data are somehow inspected and it takes a lot of time before the application is launched).
Initially I thought that displaying local images would be much easier than with remote images stored on the Internet but it seems it is rather the other way around.
Hence my questions:
Do you know why we can observe this difference (which is an inconsistency to me)?
And do you know how I could still continue to use the regular execution command ("Run Selected Line(s)")?
Best regards,
Olivier

For me the following also works when running the app via Run Selected Line(s) in RStudio:
library(shiny)
# create some local images
if(!dir.exists("myimages")){
dir.create("myimages")
}
myPlotPaths <- paste0("myimages/myplot", seq_len(3), ".png")
for (myPlot in myPlotPaths) {
png(file = myPlot, bg = "transparent")
plot(runif(10))
dev.off()
}
myImgResources <- paste0("imgResources/myplot", seq_len(3), ".png")
# Add directory of static resources to Shiny's web server
addResourcePath(prefix = "imgResources", directoryPath = "myimages")
ui <- fluidPage(
tags$img(src = myImgResources[1], width = "400px", height = "400px"),
tags$img(src = myImgResources[2], width = "400px", height = "400px"),
tags$img(src = myImgResources[3], width = "400px", height = "400px")
)
server <- function(input, output, session) {
}
shinyApp(ui, server)

Managing directories can be tricky.
You could use the here package to make things much easier to handle directories in R projects, see Ode to the here package.
After opening the project, images in www can then easily be accessed by:
here::here('www/myimage.jpg')
This will also work for sourcing an app or a script.

I don't have a specific answer, but Hadley has showed an example of how to display images from your stored locally under the 'Graphics' Chapter in 'Mastering shiny' book. The book is under development and it should be released soon, I will paste the link for that chapter:
Graphics chapter
The example is under images section.
HTH

Related

R shiny strange behaviour of caching

I am using R shiny and ggplot2.
My project layout looks like this:
global.R
server.R
ui.R
visualization.R
My visualization.R
image <- png::readPNG(source = '/path/mypng.png', native = FALSE)
ggplot(data = data) +
background_image(image)
Once I changed image path, image was changed as I was expected. When I am running visualization.R as a desktop app from R studio it works fine. But, when I am running Shiny's server.R file image is not updated. It always read same image, while image path is updated. I do not understand why.
In R studio, I also ran script rm(list = ls())
Any ideas ?
Thanks !

R Shiny - cache big dataframe

I'm quite new to Shiny, so my apologizes if my question is an easy one. I tried to check on google and stackoverflow but couldn't locate a simple and helpful answer so far.
What's my goal/issue: I'm coding a Shiny page that displays a table with hundreds of thousands of rows.
Data is sourced from different databases, manipulated, cleaned, and displayed to all the users upon request.
Problem 1: in order to load all the data, the script takes almost 5minutes
Problem 2: if at 8:00am user1 requests this data and at 8:05am user2 requests the same data, two different queries are launched and also two different spaces in memory are used to show exactly the same data to two different users.
So the question is: shall I use a cache system to enhance this process?
if not, what else shall I use?
I found a lot of official Shiny documentation on caching plots but nothing related to caching data (and I found this quite surprising).
Other useful information: data in cache should be deleted every evening around 10pm since new data will be available the next day / early morning.
Code:
ui <- dashboardPage( # https://rstudio.github.io/shinydashboard/structure.html
title = "Dashboard",
dashboardHeader(title = "Angelo's Board"),
dashboardSidebar( # inside here everything that is displayed on the left hand side
includeCSS("www/styles.css"),
sidebarMenu(
menuItem('menu 1', tabName = "menu1", icon = icon("th"),
menuItem('Data 1', tabName = 'tab_data1'))
)),
dashboardBody(
tabItems(
tabItem(tabName = 'tab_data1')),
h3("Page with big table"),
fluidRow(dataTableOutput("main_table"))
))
server <- function(input, output, session) {
output$main_tabl <- renderDataTable({
df <- data.frame(names = c("Mark","George","Mary"), age = c(30,40,35))
})
}
cat("\nLaunching 'shinyApp' ....")
shinyApp(ui, server)
Resources I used to check for potential solution:
How to cache data in shiny server? but apparently I cannot use Jason Bryer package
https://shiny.rstudio.com/reference/shiny/1.2.0/memoryCache.html but I have no idea of how to use this code applied to my example
https://shiny.rstudio.com/articles/plot-caching.html is mainly focused on plot caching
Any help would be much appreciated. Thanks
I would break out the bulk of your ETL processes into a separate R script and set that script to run on a cron. You can then have this script write out the processed dataframe(s) to a .feather file. Then have your shiny app load the feather file(s) - feather is optimized for reading so should be fast.
Example, take the necessary libraries and code out of your server.R (or app.R) file, and create a new R script called query.R. That script performs all the ETL operations and finally writes out your data to a .feather file (requires the feather package). Then create a crontab to run that script as often as needed.
Your server.R script then just needs to read in that feather file when the app loads and you should see a significant performance improvement. In addition, you have have the query.R script run during off hours so that performance on the linux box isn't negatively impacted.
Another option, put this DataFrame in global.R and change /etc/shiny-server/shiny-server.conf by adding «app_idle_timeout 0» after «location / {». This will disable application idle timeouts in Shiny Server, so global.R will be in RAM for all users.
To prevent first user from long data loading, you can put in cron «#reboot wget -O index.html localhost:3838» on your server, so on every reboot global.R will load to memory automatically.
Also, about pre-cache organisation you can read here.

How do I embed tutorial questions from 'learnr' into a full shiny app?

I am trying to embed a tutorial Rmd from the learnr package into a full shiny app. However, learnr uses the shiny_prerendered runtime, I cannot call it within my app. How do I get an interactive tutorial to run within my shiny app?
I have have three files right now: ui.R, server.R, and tutorial.Rmd.
My tutorial looks like this (one ` removed for formatting)
---
title: "my tutorial"
tutorial:
id: "com.example.tutorials.a-tutorial"
version: 1.0
output: learnr::tutorial
runtime: shiny_prerendered
---
``{r setup, include=FALSE}
library(learnr)
knitr::opts_chunk$set(echo = FALSE)
``
### Exercise Example
An R code question
``{r add-function, exercise=TRUE, exercise.lines = 5}
add <- function() {
}
``
### Quiz
R Quiz Question
``{r quiz}
quiz(
question("Question 1",
answer("wrong"),
answer("also wrong"),
answer("right", correct = TRUE),
answer("wrong again")
)
)
``
When I try rendering the output of this file from ui.R like so:
ui <- tagList(
fluidPage(theme = shinytheme("cosmo")),
navbarPage(
"appTitle",
tabPanel("Embedding Tutorials?",
includeMarkdown("tutorial.Rmd")
),
)
)
It (properly, I believe) displays it as a regular old Rmd file, not an interactive tutorial.
I've also tried using rmarkdown::render("tutorial.Rmd") which just renders the filepath to the html file generated by the Rmd (/Users/me/app/tutorial.html).
When I try to render any tutorial using run_tutorial("hello", package="learnr"), it (again, rightfully) gives the error
ERROR: Can't callrunApp()from withinrunApp(). If your application code containsrunApp(), please remove it.
I've already discovered that I can create question chunks using the question() function in learnr using the following:
ui <- tagList(
fluidPage(theme = shinytheme("cosmo")),
navbarPage(
"appTitle",
tabPanel("Tutorial",
quiz(
question("Quiz question",
answer("1"),
answer("2"),
answer("3", correct = TRUE),
answer("4"),
allow_retry = TRUE
)
),
)
)
But this does not allow the functionality of creating R code chunks that can be run within the app.
What I want is a fully interactive learnr tutorial that can be rendered from within a ui.R file for a shiny app. Is this possible?
As well as my suggestion to incorporate your extra material into the learnr tutorial I also got <iframe> embedding to work. Create an app.R with the following contents:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("learnr tutorial"),
# Show a plot of the generated distribution
mainPanel(fluidRow(
htmlOutput("frame")
))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$frame <- renderUI({
tags$iframe(
src="https://jjallaire.shinyapps.io/learnr-tutorial-03a-data-manip-filter/", width=1280, height=720
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Now when you Run App this should embed the example tutorial from https://rstudio.github.io/learnr/
It appears to be necessary for the tutorial to be rendered and published to shinyapps.io, etc.: I couldn't get it to work just from the rendered html file. So,
Create tutorial
Publish tutorial
Embed tutorial
seems to be the way forward.
Generally speaking, there are two ways to embed interactive RMarkdown documents in shiny applications.
(1) The usual way (as proposed by #Phil) is to have one R server running the embedded tutorial and another one running the application. This can be archived by deploying the tutorial via shinyapps.io or shiny-server first and then using an iframe. Alternatively, you could use callr::r_bg() to run the tutorial in a local background process. In any case, this will make it so the Rmd document can not interact with the shiny application. If this is feasable for your usecase, I would suggest this option.
(2) A more convoluted way is outlined here by the maintainer of the shinyAce package. It uses the same R server for the main application and the embedded Rmd document. See also this SO question. AFAIK, this only works with knitr::knit2html which relies on an outdated version of RMarkdown. Also, the amount of render* and output* functions available in this manner is limited unless you make sure certain JavaScript and CSS resources are properly included in your ui definition.
Quite some time has passed since I wrapped my head around this topic but my impression at the time was, that (2) takes quite a lot of work and really limits your options.

R shiny uploading a pdf from local drive does not work

I am trying to upload a pdf to shiny. If the pdf file is from the Internet, the following code works well:
library(shiny)
runApp(list(
ui = fluidPage(
sidebarLayout(
sidebarPanel(
h5("use case - embed a pdf user guide in the app - embed as a local pdf or from web URL")
),
mainPanel(
tabsetPanel(
# using iframe along with tags() within tab to display pdf with scroll, height and width could be adjusted
tabPanel("Reference",
tags$iframe(style="height:800px; width:100%; scrolling=yes",
src="https://cran.r-project.org/doc/manuals/r-release/R-intro.pdf")),
tabPanel("Summary"),
tabPanel("Plot")
)
))
),
server = function(input, output,session){}
))
However, when I tried to upload a pdf saved in Desktop, which is also the working directory, I cannot see the pdf file. I used src="example.pdf" to replaced the web file link. As suggested by some other StackOverflow posts, I saved the pdf file in a folder named www, but it still not working.
The system is MacOS X El Capiton and safari browser. I am not sure if that makes any difference.
Thanks a lot!
You have two options. The first one: just put your file example.pdf on a /www directory where your app file is. The second: use the addResourcePath function before running your app to make a local directory accessible.
addResourcePath("pdfs", "c:/temp/mypdfs")
later use it as
src="pdfs/example.pdf"

Accessing uiOutput Value On App Load

To simplify this example I've only included the necessary code to describe the issue I'm having. Should it be required, I will include a fully reproducible example, but I have a hunch that this issue can be solved through theory alone by someone with more experience using Shiny. Basically, I've programmed a Shiny app in R which looks something like this:
ui.R
plotOutput(outputId = 'heatmap1', height = "800px")
uiOutput('selectStrains')
uiOutput('selectRegions')
server.R
output$selectStrains = renderUI({
checkboxGroupInput(inputId='strains',
choices=sort(colnames(mousedata)),
selected=colnames(mousedata))
})
output$selectRegions = renderUI({
checkboxGroupInput(inputId='regions',
choices=sort(rownames(mousedata)),
selected=rownames(mousedata))
})
# more code
output$heatmap1 = renderPlot({
input$recalculate
mousedatamat = as.matrix(mousedata[isolate(input$strains), isolate(input$regions)])
heatmap.2(mousedatamat)
})
problem:
My problem is that in server.R, when the app is first loaded, input$strains and input$regions are both NULL. Therefore, mousedatamat in server.R will be a 0x0 matrix, and the heatmap will be empty on the front page of the app, which makes for a pretty poor user experience. I don't know how the ui.R and server.R files interact when an app is launched, so I'm finding it difficult to debug. To make the plot show up I either need to click a recalculate button (input$recalculate), or resize the window (but only in the horizontal dimension for some reason).
To add more mystery to the mix, I have the same heatmap figure on page 2 (I'm using a navbarPage layout), which shows up on the app load when I navigate to that tab! It is the very same code, only instead it is assigned to output$heatmap2. When I navigate back to page 1, output$heatmap1 still does not display.
I've tried placing the calls to uiOutput above plotOutput in ui.R, but the main reason I don't know how to solve this I think is because I don't know much about execution flow when an app is started. Any ideas or information on this topic?

Resources