Sorry, I'm new for Shiny. I recently trying to use the reactive program to finish one project. But now I'm facing an issue not able to figure out why this happen.
First, I create a reactive expression which lists all files in one folder.
fullFilenames <- reactive({list.files(workDir, pattern="*.csv.gz", full.names=TRUE)})
Then, I observe a button event to trigger rescan the files in the folder and update the updateCheckboxGroupInput.
I set a breakpoint at "print("File Scaned")".
When we start the app, the Checkbox Group displays all files under folder correctly.
Then I remove/add files under working folder, then click rescan button. But the Checkbox Group does not get updated.
When program stopped at the breakpoint, I checked value "fullFilenames" and "fullFilenames()", and found that fullFilenames been updated, but not fullFilenames(). I trying to understand the logic behind two values are different and find a way how to update the Checkbox Group in a right way.
Can you please give some help on this issue?
Thank you very much.
Refer to below code:
library(shiny)
library(shinydashboard)
workDir<-c("c:/files")
setwd(workDir)
ui <- dashboardPage(
dashboardHeader(title = "Test Tool"),
dashboardSidebar(
sidebarMenu(
menuItem("Setup", tabName = "setup", icon = icon("cogs"))
)
),
dashboardBody(
tabItem(tabName = "setup",
fluidRow(
box(width = 12,
h1("Setup before generate dashboard"),
p(class = "text-muted",
br(),
"Default all files under working directory been selected, you also can choice your desired files.",
br(),
br()
),
fluidRow(
column(12, align="center",
actionButton("rescanFilesBtn","Rescan CSV Files")
)
)
)
),
fluidRow(
column(width = 12,
box(width = NULL, style = "max-height: 500px", status = "info", solidHeader = TRUE,
title = "Change raw CSV files as you like",
textInput("csvFileFilter","Desired CSV Files", placeholder = "File names, separate by semicolon"),
checkboxGroupInput("selectedFiles", "",
choiceValues = NULL,
choiceNames= NULL,
selected = NULL
)
)
)
)
)
)
)
server <- function(input, output, session) {
fullFilenames <- reactive({list.files(workDir, pattern="*.csv.gz", full.names=TRUE)})
observeEvent(input$rescanFilesBtn, {
print("Scan Files")
fullFilenames <- list.files(workDir, pattern="*.csv.gz", full.names=TRUE)
updateCheckboxGroupInput(session, "selectedFiles", choices = fullFilenames)
print("File Scaned")
})
}
shinyApp(ui, server)
I have modified your code so that you get an updated list after clicking the rescan button.
In the ui part I have changed your checkboxGroupInput as follows:
checkboxGroupInput("selectedFiles", "",
choices = list.files(workDir, pattern="*.csv.gz", full.names=TRUE),
selected = NULL)
This is done because the choices cannot be NULL.
I have just commented one line in your server code as your reactive value and local variable had the same name, due to which you were not getting any value for fullFilenames()
This is the modified server:
server <- function(input, output, session) {
fullFilenames <- reactive({list.files(workDir, pattern="*.csv.gz", full.names=TRUE)})
observeEvent(input$rescanFilesBtn, {
print("Scan Files")
# fullFilenames <- list.files(workDir, pattern="*.csv.gz", full.names=TRUE)
updateCheckboxGroupInput(session, "selectedFiles", choices = fullFilenames())
print("File Scaned")
})
}
Hope this helps!
Related
Context:
I am trying to make a shiny feature where the user can upload txt files and view the content of the files as a table. To account for the different types of delimiters I have placed radio buttons to act as delimiter options. The radio buttons will depend on the file type uploaded.
Issue:
The user would not be able to change the delimiter options and display the respective table, because the server keeps checking the file type and changing the radio buttons and resetting the default button.
Attempted solutions:
Setup a radiobutton for the user to select the file type which changes the delimited buttons instead of using an if condition of file type to determine the delimiter radiobuttons. Solution is not user friendly since the program should be able to identify and modify the radiobuttons in accordance to the file type.
Setup a go button to perform the if conditions for file type instead of relying on the server looping. Couldn't implement it properly
Question: Can anyone suggest solutions that are user friendly? Is there a feature in R shiny that already solves this issue?
UI
library(shinyWidgets)
library(DT)
library(shiny)
ui <- fluidPage(
titlePanel(title=div(img(src="ODClogo.png", height = 50), "OutDeCo")),
#navbarPage is top menu bar
navbarPage("",
#tabPanel is each tab in the navbarPage
# Assess DE tab
tabPanel(
title="Assess DE",
dropdown(
# title of sidepanel
tags$h3("Options"),
# inputs in the sidepanel
fileInput("DEFile", "Choose DE File",
accept = c(
".csv",
".tsv",
".txt"
)
),
# button for selecting delimiter, default is nothing until file is selected and handled in server side
radioButtons(inputId = 'sepButton', label = 'Delimiter Selector', choices = c(Default=''), selected = ''),
# side panel characteristics
style = "gradient", icon = icon("cog"),
status = "primary", width = "300px",
animate = animateOptions(
enter = animations$fading_entrances$fadeInLeftBig,
exit = animations$fading_exits$fadeOutLeftBig
)
),
navlistPanel(
tabPanel(
title="Cluster Genes",
"Cluster genes Page",
# Navigation Bar for types of plots inside cluster
tabsetPanel(
tabPanel(
title="View file",
mainPanel(
uiOutput("UIDEContent")
)
),
tabPanel(
title="Plot 2"
),
tabPanel(
title="Plot 3"
)
),
),
),
)
),
)
Server
server <- function(input, output, session) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
# reactive converts the upload file into a reactive expression known as data
data <- reactive({
# DEFile from fileInput() function
ServerDEFile <- input$DEFile
# extensions tool for format validation
extDEFile <- tools::file_ext(ServerDEFile$datapath)
# file format checking
req(ServerDEFile)
validate(need(extDEFile == c("csv", "tsv", "txt"), "Please upload a csv, tsv or txt file."))
# convert data into file format
if(is.null(extDEFile)){return()}
if (extDEFile == "txt") {
choice <-c(Comma=",", Semicolon=";", Tab="\t", Space=" ")
updateRadioButtons(session, "sepButton",
label = paste("Delimiters for", extDEFile, "file"),
choices = choice,
)
}
else if (extDEFile == "tsv") {
choice <- (Tab="\t")
updateRadioButtons(session, "sepButton",
label = paste("Delimiter: Tab"),
choices = choice
)
}
else {
choice <- (Comma=",")
updateRadioButtons(session, "sepButton",
label = paste("Delimiter: Comma"),
choices = choice
)
}
read.table(file=ServerDEFile$datapath, sep=input$sepButton)
})
# creates reactive table called DEFileContent
output$DEFileContent <- renderTable({
if(is.null(data())){return ()}
data()
})
# handles rendering of reactive object on tb on ui
output$UIDEContent <- renderUI({
tableOutput("DEFileContent")
})
}
Use reactive object just to create a data frame, and use an observer to update radio button. Try this
library(shinyWidgets)
library(DT)
library(shiny)
ui <- fluidPage(
titlePanel(title=div(img(src="ODClogo.png", height = 50), "OutDeCo")),
#navbarPage is top menu bar
navbarPage("",
#tabPanel is each tab in the navbarPage
# Assess DE tab
tabPanel(
title="Assess DE",
dropdown(
# title of sidepanel
tags$h3("Options"),
# inputs in the sidepanel
fileInput("DEFile", "Choose DE File",
accept = c(
".csv",
".tsv",
".txt"
)
),
# button for selecting delimiter, default is nothing until file is selected and handled in server side
radioButtons(inputId = 'sepButton', label = 'Delimiter Selector', choices = c(Default=''), selected = ''),
# side panel characteristics
style = "gradient", icon = icon("cog"),
status = "primary", width = "300px",
animate = animateOptions(
enter = animations$fading_entrances$fadeInLeftBig,
exit = animations$fading_exits$fadeOutLeftBig
)
),
navlistPanel(
tabPanel(
title="Cluster Genes",
"Cluster genes Page",
# Navigation Bar for types of plots inside cluster
tabsetPanel(
tabPanel(
title="View file",
mainPanel(
uiOutput("UIDEContent")
)
),
tabPanel(
title="Plot 2"
),
tabPanel(
title="Plot 3"
)
),
),
),
)
),
)
server <- function(input, output, session) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
observe({
# DEFile from fileInput() function
ServerDEFile <- req(input$DEFile)
# extensions tool for format validation
extDEFile <- tools::file_ext(ServerDEFile$datapath)
if(is.null(input$DEFile)){return()
}else{
if (extDEFile == "txt") {
label = paste("Delimiters for", extDEFile, "file")
choice <-c(Comma=",", Semicolon=";", Tab="\t", Space=" ")
}else if (extDEFile == "tsv") {
label = paste("Delimiter: Tab")
choice <- (Tab="\t")
}else {
label = paste("Delimiter: Comma")
choice <- (Comma=",")
}
updateRadioButtons(session, "sepButton", label = label, choices = choice)
}
})
# reactive converts the upload file into a reactive expression known as data
data <- reactive({
# DEFile from fileInput() function
ServerDEFile <- input$DEFile
# extensions tool for format validation
extDEFile <- tools::file_ext(ServerDEFile$datapath)
# file format checking
req(ServerDEFile)
validate(need(extDEFile == c("csv", "tsv", "txt"), "Please upload a csv, tsv or txt file."))
# convert data into file format
if(is.null(extDEFile)){return()}
read.table(file=ServerDEFile$datapath, sep=input$sepButton)
})
# creates reactive table called DEFileContent
output$DEFileContent <- renderTable({
if(is.null(data())){return ()}
data()
})
# handles rendering of reactive object on tb on ui
output$UIDEContent <- renderUI({
tableOutput("DEFileContent")
})
}
shinyApp(ui = ui, server = server)
I'm building a simple website using Shiny,that allow users to uplaod a csv,xls ... file within Getting the data tab and view it in another tab named Viewing the data and then plot that data in another tab visualizing the data . for instance i want just to render a table based on the data picked ,
Here's a snippet of what i tried :
ui :
ui <- fluidPage(
sidebarLayout(
sidebarPanel("APRIORI INPUTS",id="panelTitle"),
mainPanel(
tabsetPanel(
tabPanel(title = "Getting the data",icon = icon("database"),
tags$div(id="uploadFiles",
fileInput("file1", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv"))
)
),
tabPanel(title = "Viewing the data",icon = icon("eye"),
tableOutput("Viewing_the_data")),
tabPanel(title = "visualizing the data",icon = icon("chart-bar"),
tableOutput("visualizing_the_data"))
)
),
)
)
for the server logic :
server :
server <- function(input, output){
output$Viewing_the_data <- renderTable({
req(input$uploadFiles)
read.csv(input$selection$datapath)
})
}
shinyApp(ui = ui, server = server)
I tried that but doesn't work ...
PS : i tried that with shinydashboard and it works perfectly as that : r shiny - display data frame after uploading
Any suggestions or advice would be appreciated. Thanks.
I've tried to adapt the example I mentioned with your app, removing some of the complexity to make it a simpler app but still have the tabbed structure and it works when I run it. I can choose the file in one tab, select how many rows to show in the sidebar and show the data in another tab:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel("APRIORI INPUTS",id="panelTitle",
numericInput("n", "Rows", value = 5, min = 1, step = 1)),
mainPanel(
tabsetPanel(
tabPanel(title = "Getting the data", fileInput("file", NULL, accept = c(".csv", ".tsv")), icon = icon("database")),
tabPanel(title = "Viewing the data", tableOutput("head"), icon = icon("eye")))
)
)
)
server <- function(input, output, session) {
data <- reactive({
req(input$file)
ext <- tools::file_ext(input$file$name)
switch(ext,
csv = vroom::vroom(input$file$datapath, delim = ","),
tsv = vroom::vroom(input$file$datapath, delim = "\t"),
validate("Invalid file; Please upload a .csv or .tsv file")
)
})
output$head <- renderTable({
head(data(), input$n)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I need to develope a shiny interface with many csv loaded in it. Based on my past experience with shiny, I prefer to import this data before the server function, in this way I hope that each session will run faster. The app will be restarted each morning, so data will be daily refreshed.
The problem is that I need to consider an extra refresh during the day, performed manualy with a button that source an external updating script.
I can't (but I hope that is possibele), refresh the data loaded at the start of the app. Below my (dummy) code:
server:
library(shinydashboard)
library(plotly)
library(data.table)
library(dplyr)
path1<-"C:/Users/.../DATA/"
path2<-"C:/Users/../DATA/csv/"
##load dataset at first start
table<-fread(file=paste0(path2,"main.csv"),data.table=FALSE))
shinyServer(function(input, output,session) {
##### refresh data with button####
observeEvent(input$refresh_data,{
source(paste0(path1,"any_script.r"),local = FALSE)
table<<-fread(file=paste0(path2,"main.csv"))
})
#####...ui####
table_r<-reactive({
##obs populate the input for choosing rows to be plotted
obs<-rev(unique(table$anycolumn))
curve_sint<-list(
lotti=obs,
data=obs
)
})
output$obs_ui<-renderUI({
selectInput("input_obs","Please choose the batch:",
choices =table()$obs ,multiple = T)
})
output$plot<-renderPlotly({
table_r()$data%>%
filter(anycolumn==input$input_obs)%>%
plot_ly(
x=~x,
y=~y,
color=~anycolumn,
type="scatter"
)
})
})
ui:
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(
title = "shiny"
),
dashboardSidebar(
width=250,
sidebarMenu(
menuItem(
"plot data"
tabName = "clhc",
icon = NULL
),
menuItem(
"Update data",
icon=icon("gear"),
tabName="update_data"
)
)
),
dashboardBody(
tabItems(
tabItem(
tabName = "clhc",
fluidRow(
column(width=3,
uiOutput("obs_ui")
),
),
fluidRow(
column(
width=12,
fluidRow(
plotlyOutput("plot")
)
)
)
),
tabItem(
tabName = "update_data",
fluidRow(
box(
width=12,
title="Sint HC",
actionButton("refresh_sint_hc","Refresh", icon=icon("refresh"))
)
)
)
)
)
)
I'm sure that the script inside observeevent works fine, because if I put a print(nrow(table)) after the fread I can see that table is correctly refreshed. I can't understand where the new data is stored because from the plot panel is stil available the old data before the update.
Is my attempt completley wrong?
Using <<- will make table accessible globally, and after terminating your shiny app, but you need it to be reactive. Here is a brief example on using a reactiveVal (setting to table1 as default) that gets modified when the actionButton is selected and a new data file is read.
library(shiny)
library(data.table)
table1 <- fread(file = 'atest1.csv')
ui <- fluidPage(
verbatimTextOutput("text"),
actionButton("refresh", "Refresh")
)
server <- function(input, output, session) {
rv <- reactiveVal(table1)
output$text <- renderText({
names(rv())
})
observeEvent(input$refresh, {
print("Refresh")
table1 <<- fread(file = 'atest2.csv')
rv(table1)
})
}
shinyApp(ui, server)
I'm trying to use the shinyFiles library in my shinyApp, in order to give the user the possibility to select a group of files or a directory.
My idea is to use a uiOutput that changes depending on a checkbox selection.
Here I report the code, that maybe is more explicative than words
UtilityUI <- fluidPage(
titlePanel("page1"),
fluidRow(
column(2,
wellPanel(
tags$p("Check the box below if you want to choose an entire directory"),
checkboxInput(inputId = 'directory_flag', label = 'Directory path?', value = FALSE),
uiOutput("input_selection_ui")
)
),
column(8
#...
)
)
)
UtilityServer <- function(input, output, session) {
output$input_selection_ui <- renderUI({
if(input$directory_flag == TRUE) {
shinyDirButton(id = "infiles", label = "Choose directory", title = "Choose a directory")
} else {
shinyFilesButton(id = "infiles", label = "Choose file(s)", title = "Choose one or more files", multiple = TRUE)
}
})
shinyFileChoose(input, 'infiles', roots=getVolumes(), session=session, restrictions=system.file(package='base'))
shinyDirChoose(input, 'infiles', roots=getVolumes(), session=session, restrictions=system.file(package='base'))
}
shinyApp(UtilityUI, UtilityServer)
The problem borns when the "shinyFiles" button is pressed: the popup window doesn't load the roots, in both cases (shinyDirButton and shinyFilesButton).
If I don't use the uiOutput function everything works well... But in that case I cannot change my UI dinamically...
Thanks a lot for your replies,
Inzirio
It seems I can't get it to work either with renderUI(). Instead I implemented the same behavior using conditionalPanel() to show alternative buttons. This seems to work. Here is the code:
ui <- shinyUI(fluidPage(
checkboxInput(
inputId = 'directory_flag',
label = 'Directory path?',
value = FALSE
),
conditionalPanel(
"input.directory_flag == 0",
shinyFilesButton(
id = "infile",
label = "Choose file(s)",
title = "Choose one or more files",
multiple = TRUE
)
),
conditionalPanel(
"input.directory_flag == 1",
shinyDirButton(id = "indir", label = "Choose directory", title = "Choose a directory")
)
))
server <- shinyServer(function(input, output, session) {
shinyFileChoose(
input,
'infile',
roots = getVolumes(),
session = session,
restrictions = system.file(package = 'base')
)
shinyDirChoose(
input,
'indir',
roots = getVolumes(),
session = session,
restrictions = system.file(package = 'base')
)
})
shinyApp(ui, server)
I am reading a csv file using shiny interface in a R markdown(RMD) file.
```{r, echo = FALSE}
shinyApp(
ui = fluidPage(
fluidRow(
column(3,
fileInput("file","Upload the file"),
helpText("Default max. file size is 5MB")
),
column(4,
tags$hr(),
h5(helpText("Select the read.table parameters below")),
checkboxInput(inputId = 'header', label = 'Header', value = TRUE),
checkboxInput(inputId = "stringAsFactors", "stringAsFactors", TRUE),
br()
),
column(5,
radioButtons(inputId = 'sep', label = 'Separator', choices = c(Comma=',',Semicolon=';',Tab='\t', Space=''), selected = ',')
),
mainPanel(
uiOutput("tb")
# use below code if you want the tabset programming in the main panel. If so, then tabset will appear when the app loads for the first time.
# tabsetPanel(tabPanel("Summary", verbatimTextOutput("sum")),
# tabPanel("Data", tableOutput("table")))
)
)
),
server = function(input, output) {
data <- reactive({
file1 <- input$file
if(is.null(file1)){return()}
read.table(file=file1$datapath, quote = NULL,header = TRUE, sep=input$sep, fill=TRUE,stringsAsFactors = input$stringAsFactors)
})
output$table <- renderTable({
if(is.null(data())){return ()}
head(data(),5)
})
output$tb <- renderUI({
if(is.null(data()))
return()
else
tabPanel("Data", tableOutput("table"))
})
},
)
```
Input data is now stored in data(). Later in my document i wish to create another shiny application and plot the histogram of this data.In this case i need to pass the variable data() to RMarkdown and later call that variable in the next shiny App. Is there any ways to do it?
Well, the solution to your problem is to create a layer application. Shiny does not work like html or php, you call the files and each file has its own code. Shiny only generates an html code (when you run the application).
Probably, you have some options to show the plot in a Shiny app:
http://shiny.rstudio.com/gallery/navbar-example.html
https://github.com/daattali/shinyjs
http://shiny.rstudio.com/reference/shiny/latest/conditionalPanel.html
In my experience, I used the navbar, to create a navigation panel, that you only see the selected menu. Moreover you can use the package shinyjs, that allows you to hide some elements, you when you want it.