r shiny create dropdown menu using folder content - r

This is my day one with R shiny and I'm trying to figure out the following question: assuming that I have a root directory like
/usr/data/
This directory might contain some folders (A,B,C,...) where each folder has contains some files (no folders within them and just files). I am planning to create a dropdown menue based on another dropdown menu. The use select a folder name from the list and the second dropdown menu is automatically loaded with the file names within that folder. For example if folder A is selected and it contains File1 and File2 then the second dropdown will contain those two. This is how I'm doing it right now:
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("This is a test app"),
sidebarLayout(
sidebarPanel(
uiOutput("Box1"),
uiOutput("Box2")
),
mainPanel("Display results",
tableOutput("view"))
)
))
server.R
library(shiny)
biz = data.frame(
Folder = c("A", "A", "B" , "B"),
Filename = c("File1","File2","File3","File4"),
stringsAsFactors = FALSE
)
shinyServer(function(input, output) {
output$Box1 = renderUI(selectInput("folder","Select directory",c("None",unique(biz$Folder)),"None"))
output$Box2 = renderUI(
if (is.null(input$folder) || input$folder == "None"){return()
}else selectInput("filename",
"Select dataset",
c("None",unique(biz$Filename[which(biz$Folder == input$folder)])),
"None")
)
subdata1 = reactive(biz[which(biz$Folder == input$folder),])
subdata2 = reactive(subdata1()[which(subdata1()$Filename == input$filename),])
output$view = renderTable({
if(is.null(input$folder) || is.null(input$filename)){return()
} else if (input$folder == "None" || input$filename == "None"){return()
} else return(subdata2())
})
})
As you can see, I'm using a dataframe to take care of that but this should better be dynamic in case that the content of those folders change. So I think the best way is to have list of folder names and then get the contents of that folder and load them in to the second dropdown. I have tried several posts on SO but they are mostly about the content of a csv file and so on. Any help would be much appreciated.
Thanks

I recommend generating UI elements reactively in the following way:
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("My Great File Selector"),
fluidRow(
sidebarPanel(
uiOutput("select.folder"),
uiOutput('select.file')
)
)
))
server.R
library(shiny)
shinyServer(function(input, output) {
root <- '~'
output$select.folder <-
renderUI(expr = selectInput(inputId = 'folder.name',
label = 'Folder Name',
choices = list.dirs(path = root,
full.names = FALSE,
recursive = FALSE)))
output$select.file <-
renderUI(expr = selectInput(inputId = 'file.name',
label = 'File Name',
choices = list.files(path = file.path(root,
input$folder.name))))
})
All I have done in ui.R is essentially promise that I will render two UI elements named 'select.folder' and 'select.file' in server.R. That's pretty simple.
In server.R, I specify root, where I want the app to look for directories.
I use renderUI() to generate a UI element in which the user will select the folder and store it in output$select.folder, fulfilling my first promise in ui.R, give it a label of 'Folder Name', and set its choices to the result of list.dirs() (from base R) called with the appropriate arguments. The choice that the user has selected in this UI element will be accessible via input$folder.name because the InputId is 'folder.name'.
I use renderUI() again to generate a UI element that will be used to select a file in that folder and store it in output$select.file, fulfilling my second promise from ui.R. Its choices are based on a call to list.files that uses the folder selected in the first UI element, retrieving it via `input$folder.name'.

You can get the names of the files in a folder with the list.files function.
So something like this should work:
ROOT <- '/usr/data'
dropdown_options <- list.files(paste(ROOT, input$folder, sep = '/'))

Related

R/Shiny: Populate CheckBox Group Dynamically Using Headers From Uploaded File

I'm trying to build a page using R Shiny that has:
A File Widget for uploading CSV files
A Checkbox Group component
I'd like to use these as follows:
Upon uploading a valid CSV file, populate the CheckBox group whose checkboxes are all the headers from the CSV file, all checked by default
I've tried various forms of observe() and observeEvent() so far, but have had no success in getting this even close. Any suggestions you may have would be great.
You may try checkboxGroupInput.
library(shiny)
ui <- fluidPage(
fileInput('file', 'Upload csv file'),
uiOutput('dropdown')
)
server <- function(input, output) {
data <- reactive({
req(input$file)
read.csv(input$file$datapath)
})
output$dropdown <- renderUI({
req(data())
checkboxGroupInput('cols', 'Select Column', names(data()),
names(data()), inline = TRUE)
})
}
shinyApp(ui, server)
You can set inline = FALSE if you want to arrange them vertically.

Get original names of files with a function like fileInput (R shiny)

I want to select some files in the browser like with fileInput in Shiny but I only need their paths as a character and nothing else. I do not want to upload them (but it's no problem if it is done anyway). When I use fileInput the result is a data.frame containing the paths of the files in a temporary folder with the names i.e. 0.csv, 1.txt, 2.pdf ... But I need the original filenames (with or without the full path). Is there any way to achieve this in a fast and 'non-hacky' way?
There is a very important reason why this is not possible: Security
JavaScript has no accress to the file System, so you will not to able to get the full paths of the user. One option is your force your user to use a path, but well... he can lie there of course. Maybe do it like this
You could only use it like this:
library(shiny)
ui <- fluidPage(
tags$h1("Test"),
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
textInput("path", "Please enter the full path of your file"),
tableOutput("pathtable")
)
server <- function(input, output, session) {
testdf <- reactive({
data.frame(
ID = 1,
file = input$path
)
})
output$pathtable <- renderTable({
if(input$path == "") {
return(NULL)
} else {
testdf()
}
})
}
shinyApp(ui = ui, server = server)
The original names are saved in the variable
input$file1$name
However the "real" data (which is renamed as OP correctly pointed out) can be accessed via
input$file1$datapath
where file1 is the InputId of the function fileInput()

R Shiny Reactively Display an Image from a list

I am trying to reactively display an image from a list in an R shiny application.
I have many .tiff images stored in the "www" directory of my app. They follow the naming convention OHC_130.tiff, OHC_131.tiff, OHC_132.tiff, IHC_133.tiff, Deiter_134.tiff, OHC_135.tiff etc...
I also have a vector containing all of these names
ImgID_Vector <- as.vector(c("OHC_130", "OHC_131", "OHC_132", "IHC_133", "Deiter_134", "OHC_135")
I would like to make a selectable input dropdown list like this where a user can select an image and then click the submit button to make the image appear below. I have set this up for the ui.r but i am not sure how to make it work on the server side.
#ui.r
library(shiny)
library(shinydashboard)
dashboardBody(
tabItems(
tabItem(tabName = "dt",
h2("Select an image"),
fluidRow(
box(title="This is a searchable database of images", solidHeader = TRUE,status = "primary"),
selectInput("input$ImageIDVariable1", label = h4("Enter your image of interest"), choices = (ImgID_Vector), multiple = TRUE),
submitButton("Submit"),
imageOutput("ImageID_Image")
)
)
)
Conceptually I know that on the server side I need to connect the user input from the UI side to the actual image in the "www" folder. I should be able to do this using reactive inputs and renderImage I think. But I am not sure how to write the render image command to achieve the desired result.
#server.r
#This is the data that contains the choices for the dropdown menu
ImgID_Vector <- readRDS("ImgID_Vector.RDS")
shinyServer(function(input, output) {
# This is where I am struggling, with the render image command
output$ImageID_Image <- renderImage({
filename <- normalizePath(file.path('./www',
paste(input$ImageIDVariable1, '.tiff', sep='')))
list(src = filename)
}, deleteFile = FALSE)
}
#This is where I have the reactive input variable
ImageIDVariable1 <- reactive({input$ImageIDVariable1})
})
Thanks for your help!
Hi argument inputId from your selectInput is wrong, it should be "ImageIDVariable1", not input$ImageIDVariable1.
In ui.R :
selectInput(inputId = "ImageIDVariable1", label = h4("Enter your image of interest")
In server.R
input$ImageIDVariable1
Moreover :
You should use this in a script called global.R or at least in ui.R :
ImgID_Vector <- readRDS("ImgID_Vector.RDS")
And you should not use multiple = TRUE because renderImage can only render one image at a time.
And you should put a selected choice by default, if not renderImage will search an image which doesn't exist.

Interactive directory input in Shiny app (R)

I am building a shiny app that requires a user to select a folder on the local machine, which contains the files to be processed by the app.
I am using a solution proposed here. This works fine on a local machine, but does not work if the app is deployed to a shinyapps server.
The author of this solution confirmed that it was only designed to work with local Shiny apps, since it makes OS shell calls to display a directory dialog.
I am wondering if there is a different solution for directory dialog, which will work on the deployed Shiny apps (I am deploying to shinyapps.io).
Edited: Notice that I cannot use fileInput interface for two reasons:
The users of the app are not technical people and they do not know which files inside the folder are used by the app.
The selected folder may contain other folders within which the needed files reside, such that it is impossible to select all files at once, even if the fileInput interface has the multiple option enabled.
The folder/files structure is not something I can change, it is downloaded AS IS from a medical device and therefore the only thing I can expect from the users is to specify the parent folder and the rest should be done inside the R code.
This is a working example based on using the "webkitdirectory" attribute. At the moment the attribute is supported by Chrome, Opera and Safari (mobile and desktop) and it should be supported in Firefox 49 to be released in September.
More about this here. It work with subdirectories also.
It requires using the tags keyword in ui.R. I have tested it by uploading three csv files each contaning three numbers separeted by a coma. Tested locally and on shinyapps.io with Chrome and Opera. This is the code:
ui.R
library(shiny)
library(DT)
shinyUI(tagList(fluidPage(theme = "bootstrap.css",
includeScript("./www/text.js"),
titlePanel("Folder content upload"),
fluidRow(
column(4,
wellPanel(
tags$div(class="form-group shiny-input-container",
tags$div(tags$label("File input")),
tags$div(tags$label("Choose folder", class="btn btn-primary",
tags$input(id = "fileIn", webkitdirectory = TRUE, type = "file", style="display: none;", onchange="pressed()"))),
tags$label("No folder choosen", id = "noFile"),
tags$div(id="fileIn_progress", class="progress progress-striped active shiny-file-input-progress",
tags$div(class="progress-bar")
)
),
verbatimTextOutput("results")
)
),
column(8,
tabsetPanel(
tabPanel("Files table", dataTableOutput("tbl")),
tabPanel("Files list", dataTableOutput("tbl2"))
)
)
)
),
HTML("<script type='text/javascript' src='getFolders.js'></script>")
)
)
server.R
library(shiny)
library(ggplot2)
library(DT)
shinyServer(function(input, output, session) {
df <- reactive({
inFiles <- input$fileIn
df <- data.frame()
if (is.null(inFiles))
return(NULL)
for (i in seq_along(inFiles$datapath)) {
tmp <- read.csv(inFiles$datapath[i], header = FALSE)
df <- rbind(df, tmp)
}
df
})
output$tbl <- DT::renderDataTable(
df()
)
output$tbl2 <- DT::renderDataTable(
input$fileIn
)
output$results = renderPrint({
input$mydata
})
})
text.js
window.pressed = function(){
var a = document.getElementById('fileIn');
if(a.value === "")
{
noFile.innerHTML = "No folder choosen";
}
else
{
noFile.innerHTML = "";
}
};
getFolders.js
document.getElementById("fileIn").addEventListener("change", function(e) {
let files = e.target.files;
var arr = new Array(files.length*2);
for (let i=0; i<files.length; i++) {
//console.log(files[i].webkitRelativePath);
//console.log(files[i].name);
arr[i] = files[i].webkitRelativePath;
arr[i+files.length] = files[i].name;
}
Shiny.onInputChange("mydata", arr);
});
Let me know if this helps.
Have you tried around with the shinyFiles package?
There is a widget which lets you chose a directory.
As output you get the path of that directory which you can in turn use to access the files.
Here is an example how it works.
server
library(shiny)
library(shinyFiles)
shinyServer(function(input, output, session) {
# dir
shinyDirChoose(input, 'dir', roots = c(home = '~'), filetypes = c('', 'txt'))
dir <- reactive(input$dir)
output$dir <- renderPrint(dir())
# path
path <- reactive({
home <- normalizePath("~")
file.path(home, paste(unlist(dir()$path[-1]), collapse = .Platform$file.sep))
})
# files
output$files <- renderPrint(list.files(path()))
})
ui
library(shiny)
library(shinyFiles)
shinyUI(fluidPage(sidebarLayout(
sidebarPanel(
shinyDirButton("dir", "Chose directory", "Upload")
),
mainPanel(
h4("output$dir"),
verbatimTextOutput("dir"), br(),
h4("Files in that dir"),
verbatimTextOutput("files")
)
)))
Hope this helps.

create multiple shiny widgets with data from uploaded file

In shiny, how do you use tagList inside renderUI to create multiple widgets customized with data from an uploaded file? This idea is referenced here, but there doesn't appear to be very good documentation for tagList.
I plan to answer my own question here. I did a bit of research, found that a simple example of this process was lacking, and felt a desire to contribute it so that others might benefit.
In server.R, define an object using a reactive() statement to hold the contents of the uploaded file. Then, in a renderUI statement, wrap a comma-delimited list of widget definitions in a tagList function. In each widget, use the object holding the contents of the uploaded file for the widget parameters. The example below, hosted at shinyapps.io and available on github, creates a checkBoxGroupInput and a radioButtons widget using a singer renderUI that is defined based on an uploaded file.
server.R
library(shiny)
shinyServer(function(input, output) {
ItemList = reactive(
if(is.null(input$CheckListFile)){return()
} else {d2 = read.csv(input$CheckListFile$datapath)
return(as.character(d2[,1]))}
)
output$CustomCheckList <- renderUI({
if(is.null(ItemList())){return ()
} else tagList(
checkboxGroupInput(inputId = "SelectItems",
label = "Which items would you like to select?",
choices = ItemList()),
radioButtons("RadioItems",
label = "Pick One",
choices = ItemList(),
selected = 1)
)
})
})
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("Create a checkboxGroupInput and a RadioButtons widget from a CSV"),
sidebarLayout(
sidebarPanel(fileInput(inputId = "CheckListFile", label = "Upload list of options")),
mainPanel(uiOutput("CustomCheckList")
)
)
))

Resources