I have a check box control defined in my UI which is reading excel files from a directory and displaying for loading. User selects the file and there is a function defined which loads its data into data frame.
Issue i am facing is that once application starts any addition/deletion of files in the directory is not reflected into checkbox i.e. files read at the start of application are always displayed.
Kindly share any solution.
Regards
Here is one possible option. We use invalidateLater(1000) to invalidate the observer every second, and we use a reactiveVal to keep track of the files currently as choices in the selectInput, so we only re-render the input if the files have changed. Hope this helps!
library(shiny)
ui <- fluidPage(
selectInput('input1','Files: ', NULL)
)
server <- function(input,output, session)
{
# To keep track of current files
prev.files<-reactiveVal(NULL)
observe({
invalidateLater(1000)
isolate({
new_files <- list.files()
# If files have changed...
if(!isTRUE(all.equal(new_files,prev.files())))
{
print('Updating selectInput')
updateSelectInput(session,'input1',choices=new_files) # update selectInput
prev.files(new_files) # Update prev.files()
}
})
})
}
shinyApp(ui,server)
Related
I am developing a shiny app that is a tool to allow users to map and view photos with the goal of assigning unique ID numbers to unique images in a given area. The tool maps the location of the photos (latitude/longitude) with symbology differentiating photos that have been viewed before (I,e, given a unique ID number and therefore is ‘known’) or have not been viewed yet. Users selectInput attributes about the image (type, color, etc) and the tool filters and displays the photos of all ‘known’ photos based on those attributes; the hope is that if the building has been photographed before, then its previously assessed photos will be displayed including the assigned ID number of that building. The last attribute the user enters is a self-assessment of the photo and whether that building is new to the study (and a new ID number is then assigned) or is already known. If the photo is of a known building, the user enters the ID number of that building. The user then clicks an action button to record and save the data and the map then automatically refreshed with the saved data (hence the previously assessed photo is now known and the symbology updates appropriately).
I want users to be able to practice/learn the app with some supplied practice data as well as be able to upload and use the tool with their own dataset. The file needs to be saved and reloaded regularly so that the map updates and reflects those images that already been assessed. To this end, I have used a reactiveFileReader function to read in the data. The problem I am facing is defining the file name to alternate between either supplied or user-provided data within the reactiveFileReader call. One other important thing with my app, which I acknowledge is not elegant, is that users need to indicate the folder path to where their data is located both to allow the file to be saved and re-read but also so their photos can be accessed to display. Having users enter their folder path is pretty cumbersome and the requirement they change backslash to forward slash is not ideal but the best I can do at the moment.
The code below seems to function properly except that when users enter their folderpath, the reactive_data call does not seem to actually call the data file despite my checkit which prints the path to the correct file. My thought was that the thefile() call within the reactiveFileReader function is not updating but the two attempts I have in the code have not worked. Any suggestions would be appreciated.
library(shiny)
library(stringr)
library(shinyFiles)
# UI ----
ui <- fluidPage(mainPanel(
p("I don't have my own data, but let me try the tool"),
actionButton("SampleData", "Let me try the tool!",style="color: #fff; background-color: #1d4568; border-color: #2e6da4"),
br(), br(), br(),
p("I have my own data file saved as 'photoID.csv'."),
p("please copy the path to the folder where your data file resides",strong("BUT we need you to make one change and this
is really important: please change backslash to front slash '/' in the path name."),"We need this information so that as you enter data in the
app, the datafile will save automatically"),
textInput("folderpath", "Path to folder"),
h4("My check to see what path is being read:"),
textOutput("check"), # This prints the correct route to the file...
h4("Photo data spreadsheet (first few lines)"),
tableOutput("table_fileR"))
)
# SERVER ----
server <- function(input, output, session) {
# FIRST ATTEMPT BUT IT DIDN'T WORK
thefile <- reactive({
if(nchar(input$folderpath)==0){
"./data/photoID_Sample.csv"
}else{
file.path(paste0(input$folderpath,"/photoID.csv"))
}
})
# SECOND ATTEMPT THAT DIDN'T WORK EITHER
# observe({
# invalidateLater(100)
# # Do something each time this is invalidated.
# thefile <-
# if(nchar(input$folderpath)==0){
# "./data/photoID_Sample.csv"
# }else{
# file.path(paste0(input$folderpath,"/photoID.csv"))
# }
# })
# this is the reactive data set call needed in all outputs and functions in the extended app
reactive_data <- reactiveFileReader(500, session, thefile(), read.csv)
# Datafile table
observeEvent(input$folderpath, {
output$table_fileR <- renderTable({
if(nchar(input$folderpath)==0){
return()
}else{
dat <- reactive_data()
return(tail(dat,3)) # Does not update dat with user-supplied data, rather still sample data
# return(tail(datasets::mtcars,3)) # works as a test when not calling reactive_data()
}
})
})
observeEvent(input$SampleData, {
output$table_fileR <- renderTable({
if(is.null(reactive_data())){
return()
}else{
dat <- reactive_data()
return(head(dat,2)) # works
#return(head(datasets::Puromycin,2)) # works as a test when not calling reactive_data()
}
})
})
# Path check
observeEvent(c(input$folderpath, input$SampleData), {
output$check <- renderText({
checkit <- thefile()
return(checkit)
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
I wrote a R script (MAIN.R) that converts PDF tables to CSV. When I run MAIN.R as an individual file, it functions well. I've tried it many times.
Currently, I'm working on a R shiny app that uses "MAIN.R" as a source and takes a pdf file as input. When I push the submit button, the output should appear in the MAIN panel. Unfortunately, the submit button does not function as intended.
May anyone please assist me with this, as I am new to Shiny?
UI.R
shinyUI(fluidPage(
titlePanel("DATASET CONVERSION"),
sidebarLayout(
fileInput("filein", label = h2("Select a file to convert.")),
submitButton("Submit")
),
mainPanel(
tableOutput("Dataset")
)
)
)
Server.R
source("MAIN.R")
shinyServer(function(input, output) {
outputdf <- reactive({ input$filein
})
output$Dataset <- renderTable({
outputdf()
})
})
Your Submit button is not currently linked to anything, so it will not do anything. If I am reading the code right, you are just taking the input dataset and storing it as the output of outputdf. Your output$Dataset then just picks up that outputdf and displays it as-is, without any work being done on it.
You use an action button like so:
## In UI.R
actionButton("execute", "Execute the Main Function")
## In Server.R
observeEvent(input$execute, {
## Do stuff here
})
Note that the actionButton has two parameters, inputID (which is how you refer to it) and text to display on top. For example, with input$filein, 'filein' is the inputID.
In Server.R, observeEvent won't do anything until it detects a change in input$execute, which happens when someone clicks the button. That is where you put your code to do stuff.
Now, in output$Dataset, you need to access the results of whatever you did in that observeEvent. One way to do that is to use a reactiveValue. This is just like a reactive, but instead of a function, it stores a data element. Initialize it as an empty dataframe, and then update it in the observeEvent. Something like this:
## In Server.R
treated_output <- reactiveValue(data.frame())
observeEvent(input$execute, {
## Run the function on the file
updated <- main_function(input$filein)
# Update your reactiveValue
treated_output(updated)
})
output$Dataset <- renderTable({
treated_output()
})
Does this make sense?
I'm trying to write tests in order to check if a shiny function fileInput() is reading files correctly.
My problem is that I don't know what to write in session$setInputs() in order to grab the file from my system.
Here is an example app:
library(shiny)
ui <- fluidPage(
tagList(
fileInput("file", "Please upload a file"),
tableOutput("text")
)
)
server <- function(input, output, session){
file <- reactive({input$file})
output$text <- renderTable({
req(file())
read.csv(file()$datapath)
})
}
shinyApp(ui, server)
Now, I want to be able to use testServer() in order to set a file address and see if my app loads it correctly, but I can't figure out how to do it:
address <- "path/to/text.csv"
testServer(server, {
session$setInputs(file = address)
print(file())
})
I think it has to do with the fact that fileInput() uploads the file to a temp folder and returns to shiny a dataframe where you can get the datapath, but I'm unable to simulate this pass in order to make the test work
I have the same question as you do, I did some investigating and could not find any way of testing fileInput with testServer or testthat. The best solution that I found was testing fileInput by taking a snapshot when recording a test with recordTest() of the shinytest package.
Sorry for answering this late.
I asked the same question at rstudio's forums and got an answer here
The basics of it are setting the file's datapath as a list:
address <- "path/to/text.csv"
testServer(server, { session$setInputs(file= list(datapath = address)) })
I am new to R and certainly very new to RShiny.
I wrote a packages which logs events into a log file. Rstudio is capable of viewing live logging unitl the file is 5MB. So now i am thinking about writing a Rshiny app that views the logs as they are being written to the file.
Which functions would help me to update the viewer?
Thanks!
You can call invalidateLater inside a reactive when you import the data. Data will be refreshed every time invalidateLater will fire (in my case every second).
Here a really silly example (my .csv doesn't update, it just prints that data is being refreshed to console):
library(shiny)
ui <- fluidPage(
tableOutput("data")
)
server <- function(input, output, session) {
# mtcars.csv will be read every second
mtcars_df <- reactive({
invalidateLater(1000, session)
read.csv("mtcars.csv")
})
# mtcars_df is a reactive, hence will force the table to re-render
output$data <- renderTable({
print(paste("Table refreshed at", Sys.time(), collapse = " "))
mtcars_df()
})
}
shinyApp(ui, server)
I would like to create a storage to store and retrieve uploaded files even after fileInput is reset.
Basically every time user uploads a file with fileInput, in the future he is forced to click on doneUpload button which resets the fileInput text area and puts his file in the storage (no alternatives please).
At this point, user can go back and upload another file which will be put in the storage too.
From my point of view, I have all uploaded files which I can easily retrieve and manipulate.
In other words, my question could be translated to:
how can I reset fileInput text area without resetting uploaded files?
I have tried creating a reactive value to store uploaded files, however it does not work as I expected.
Here I show you an example to simplify my situation.
library(shiny)
ui<-fluidPage(
uiOutput("uploadFile"),
actionButton("doneUpload","Done"),
tableOutput("exampleTest"))
server<-function(input, output){
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
observeEvent(input$doneUpload,{
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
})
reactiveFileUploaded<-reactiveValues(fileUploaded=NULL)
observe({
req(input$uploadFile)
reactiveFileUploaded$fileUploaded<-c(reactiveFileUploaded$fileUploaded,input$uploadFile$datapath)
})
#Test to verify if the storage has been created.
#Supposing two files have been uploaded, I retrieve the first one.
output$exampleTest<-renderTable({
read.csv2(reactiveFileUploaded$fileUploaded[1])
})
}
shinyApp(ui=ui,server=server)
Here is a much simpler approach for what you need - Just set multiple = TRUE in fileInput() so that it can accept multiple files at the same time. They can all be read and stored in a reactive in one go using lapply.
library(shiny)
ui <- fluidPage(
fileInput("files", "Upload", multiple = TRUE),
actionButton("done", "Done Upload"),
verbatimTextOutput("test")
)
server <- function(input, output) {
all_data <- eventReactive(input$done, { # stores data in all files as a list of dataframes
req(input$files)
setNames(lapply(input$files$datapath, read.csv), sapply(input$files$name, basename))
})
output$test <- renderPrint({
# all_data()[[1]] # access first file; others can be accessed the same way
lapply(all_data(), head) # shows first 6 rows of all files with their names
})
}
shinyApp(ui, server)
Quite Simple, just read the file before clearing the input, after the User has confirmed the Input.
Currently you are reading (and saving) the file the moment the User has selected it, no matter if he wants to upload it or not. Also it seems like you are somehow triggering a loop after selecting a file constantly adding the file to the reactive value.
Also with the datapath you are saving only the filepath not the filecontent, and the filepath also directs to the temp folder so I would recommend changing that routine as well.
But for the most basic functionalities you described the following code should get the job done. (I also added a simple check for content in the filesUploaded variable, so that the error message doesn't appear on start up)
library(shiny)
ui<-fluidPage(
uiOutput("uploadFile"),
actionButton("doneUpload","Done"),
tableOutput("exampleTest"))
server<-function(input, output){
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
reactiveFileUploaded<-reactiveValues(fileUploaded=c())
observeEvent(input$doneUpload,{
req(input$uploadFile)
reactiveFileUploaded$fileUploaded<-c(reactiveFileUploaded$fileUploaded ,input$uploadFile$datapath)
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
})
#Test to verify if the storage has been created.
#Supposing two files have been uploaded, I retrieve the first one.
output$exampleTest<-renderTable({
if(length(reactiveFileUploaded$fileUploaded)>1)
read.csv2(reactiveFileUploaded$fileUploaded[1])
})
}
shinyApp(ui=ui,server=server)