I modified the example here to include a save button as well. I want the user to be able to reset to the initial table after uploading a new file by adding a reset button (similar to the save button), but I wonder if it's possible to do so.
EDIT:
I want the button to be a part of the DT table and be placed next to the save.
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
fileInput("upload", NULL, accept = c(".csv")),
DTOutput("head")
)
server <- function(input, output, session) {
rv <- reactiveValues(
dataframe = data.frame(
x = seq(1:12),
y = LETTERS[1:12])
)
observe({
req(input$upload)
ext <- tools::file_ext(input$upload$name)
rv$dataframe <- switch(ext,
csv = read.csv(input$upload$datapath),
NULL)
})
output$head <- renderDT({
validate(need(!is.null(rv$dataframe)," Please upload a .csv file"))
datatable(rv$dataframe, extensions = 'Buttons',
options = list(
dom = 'Bfrtip',
buttons = list(list( extend = 'csv',
filename = '//public/comments/comments.csv',
text = 'Save'))))
})
}
shinyApp(ui, server)
Here is a snapshot of the current version:
There could be multiple ways to handle this. Here is one of it -
Used a fixed dataframe mtcars[1:6, 1:6] as the default dataframe instead of one which generates random numbers which is difficult to compare imo.
Added an actionButton for Reset feature.
Created another reactive variable called dataframe_copy which always holds the default dataframe.
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
fileInput("upload", NULL, accept = c(".csv")),
DTOutput("head"),
actionButton('reset', 'Reset')
)
server <- function(input, output, session) {
rv <- reactiveValues(
dataframe = mtcars[1:6, 1:6],
dataframe_copy = mtcars[1:6, 1:6]
)
observe({
req(input$upload)
ext <- tools::file_ext(input$upload$name)
rv$dataframe <- switch(ext,
csv = read.csv(input$upload$datapath),
NULL)
})
observeEvent(input$reset, {
rv$dataframe <- rv$dataframe_copy
})
output$head <- renderDT({
validate(need(!is.null(rv$dataframe)," Please upload a .csv file"))
datatable(rv$dataframe, extensions = 'Buttons',
options = list(
dom = 'Bfrtip',
buttons = list(list( extend = 'csv',
filename = '//public/comments/comments.csv',
text = 'Save'))))
})
}
shinyApp(ui, server)
Related
This is a follow-up question to this
Now I somehow managed to download the reactive dataframe to my hard drive (!not server or working directory) and append each new entry as new line with write.table.
Interestingly write.csv does not work because it does not allow append argument https://stat.ethz.ch/pipermail/r-help/2016-August/441011.html.
With this minimal working app, I would like to know how I can get the user to choose a directory and a filname to download there. Now I have this absolut path: file = "C:/Users/yourname/Downloads/my_df.csv" which works. But I don't know if it will work for other user!
library(shiny)
library(shinyWidgets)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width = 4,
sliderInput("a", "A", min = 0, max = 3, value = 0, width = "250px"),
actionButton("submit", "Submit")
),
mainPanel(
titlePanel("Sliders"),
tableOutput("values")
)
)
)
server <- function(input, output, session) {
sliderValues <- reactive({
data.frame(Name = c("A"), Value = as.character(c(input$a)), stringsAsFactors = FALSE)
})
output$values <- renderTable({
sliderValues()
})
# Save the values to a CSV file on the hard disk ----
saveData <- reactive({write.table(sliderValues(), file = "C:/Users/yourname/Downloads/my_df.csv", col.names=!file.exists("C:/Users/yourname/Downloads/my_df.csv"), append = TRUE) })
observeEvent(input$submit, {
saveData()
})
}
shinyApp(ui, server)
The requirement is that the user should see a modal dialog ui with the question "In which folder with which filename you want to download?".
Quasi like the things we do daily if we download from the internet.
I now solved it this way:
I realized that I have two options:
As suggested by #Stéphane Laurent using downloadhandler
Using DT::datatable()
I have decided to use number 2. Many thanks to all of your inputs!
library(shiny)
library(shinyWidgets)
library(DT)
ui <- fluidPage(
sliderInput("a", "A", min = 0, max = 3, value = 0, width = "250px"),
titlePanel("Sliders"),
dataTableOutput("sliderValues", width="450px")
)
server <- function(input, output, session) {
sliderValues <- reactive({
df <- data.frame(
Name = "A",
Value = as.character(c(input$a)),
stringsAsFactors = FALSE) %>%
pivot_wider(names_from = Name, values_from = Value)
return(df)
})
# Show the values in an HTML table in a wider format----
output$sliderValues <- DT::renderDataTable({
DT::datatable(sliderValues(),
extensions = c('Buttons'),
options = list(
dom = 'frtBip',
buttons = list(list(extend = 'excel', filename = paste0("myname-", Sys.Date())))
)
)
})
}
shinyApp(ui, server)
As I am new to R shiny, please go easy on me:
I have found this code useful: https://community.rstudio.com/t/download-dataset-filtered-in-shiny-input/75770. This code takes Iris data and filters based on the column 'Species'
In order to get the filtering results after uploading my own data via fileInput() I made some adjustments to the code above. I am trying to filter data using the column 'Type', but I am receiving the below-mentioned error.
Error:
object 'file1' not found
csv data:
ID Type Range
21 A1 100
22 C1 200
23 E1 300
code:
library(tidyverse)
library(shiny)
library(DT)
library(shinyWidgets)
ui <- fluidPage(
#setBackgroundColor(color = c("#66e0ff", "#00a3cc", "#003d4d")),
h1("Data"),
sidebarLayout(
sidebarPanel(fileInput("file1", label = "Choose species"),
downloadButton("download1","Download entire Table as csv")),
mainPanel(h4("Table 1: Iris"),
dataTableOutput("csv_dto")
)
))
server <- function(input, output, session) {
output$csv_dto <- renderTable({
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
read.csv(file$datapath, header = input$header)
})
thedata <- reactive({
file$datapath %>%
filter(Type == input$Type)
})
output$type_dto <- renderDataTable({
thedata() %>%
datatable(extensions = 'Buttons',
options = list(
#Each letter is a dif element of a datatable view, this makes buttons the last thing that's shown.
buttons = c("copy", "csv", "pdf")),
filter = list(
position = 'top'),
rownames = FALSE)
})
output$download1 <- downloadHandler(
filename = function() {
paste("type_", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(thedata(), file)
}
)
}
shinyApp(ui, server)
could someone help me fix this issue?
I can't help you with the PDF output but to get you started: You have to do some adjustments of the code in the examples from ?fileInput.
Instead of renderTable use reactive. Also do not assign to an output. Instead of dataTableOutput("csv_dto") use dataTableOutput("type_dto") in the UI.
library(tidyverse)
library(shiny)
library(DT)
library(shinyWidgets)
ui <- fluidPage(
# setBackgroundColor(color = c("#66e0ff", "#00a3cc", "#003d4d")),
h1("Data"),
sidebarLayout(
sidebarPanel(
fileInput("file1", label = "Choose species"),
downloadButton("download1", "Download entire Table as csv")
),
mainPanel(
h4("Table 1: Iris"),
dataTableOutput("type_dto")
)
)
)
server <- function(input, output, session) {
csv_dto <- reactive({
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
read.csv(file$datapath)
})
thedata <- reactive({
csv_dto() %>%
filter(Type == input$Type)
})
output$type_dto <- renderDataTable({
thedata() %>%
datatable(
extensions = "Buttons",
options = list(
# Each letter is a dif element of a datatable view, this makes buttons the last thing that's shown.
buttons = c("copy", "csv", "pdf")
),
filter = list(
position = "top"
),
rownames = FALSE
)
})
output$download1 <- downloadHandler(
filename = function() {
paste("type_", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(thedata(), file)
}
)
}
shinyApp(ui, server)
I use Timevis package.
first of all I read an excel file with missions.
In my code the user can see all the missions on a time line, and he can edit/add/remove any missions.
after the user make a change I can see the update table below.
I want to save to my excel file every update that the user make.
this is my code:
library(shiny)
library(timevis)
library(readxl)
my_df <- read_excel("x.xlsx")
data <- data.frame(
id = my_df$id,
start = my_df$start,
end = my_df$end,
content = my_df$content
)
ui <- fluidPage(
timevisOutput("appts"),
tableOutput("table")
)
server <- function(input, output) {
output$appts <- renderTimevis(
timevis(
data,
options = list(editable = TRUE, multiselect = TRUE, align = "center")
)
)
output$table <- renderTable(
input$appts_data
)
}
shinyApp(ui, server)
You can use actionButton/ observe to call saveworkbook (package openxlsx) to save your changes. Technically you are not saving these changes, but replacing the file with an identical file containing the changes.
library(shiny)
library(openxlsx)
library(timevis)
library(readxl)
my_df <- read_excel("x.xlsx")
data <- data.frame(
id = my_df$id,
start = my_df$start,
end = my_df$end,
content = my_df$content
)
mypath = paste0(getwd(), "/x.xlsx") # Path to x.xlsx
ui <- fluidPage(
timevisOutput("appts"),
tableOutput("table"),
actionButton("save", "Save")
)
server <- function(input, output) {
output$appts <- renderTimevis(
timevis(
data,
options = list(editable = TRUE, multiselect = TRUE, align = "center")
))
observeEvent(input$save,
{
my_df<- createWorkbook()
addWorksheet(
my_df,
sheetName = "data"
)
writeData(
wb = my_df,
sheet = "data",
x = input$appts_data,
startRow = 1,
startCol = 1
)
saveWorkbook(my_df, file = mypath,
overwrite = TRUE)
})
output$table <- renderTable(
input$appts_data
)
}
shinyApp(ui, server)
I have a shiny app that uploads pdfs to do some checks on them and write a report to a table for the user to see. One of the requirements is to create a link to the document that downloads the initial uploaded pdf. Is there a way to access the temp directory files for download and put that download link in a DT datatable? I've tried coping files to www and they can be accessed that way but when the session ends the files are not deleted.
library(shiny)
library(DT)
ui <- fluidPage(
fileInput('pdfFile',
'Upload PDF',
multiple = TRUE,
accept = c('.pdf')),
DTOutput('Table')
)
server <- function(input, output) {
output$Table <- renderDT({
pdfTable <- cbind(input$pdfFile,
pdflink = sprintf('%s',
input$pdfFile$datapath,
input$pdfFile$name,
input$pdfFile$name),
stringsAsFactors = FALSE)
datatable(pdfTable,escape = FALSE)
})
}
shinyApp(ui = ui, server = server)
You can use session$onSessionEnded to execute some code after the client has disconnected (I confess I never tried):
server <- function(input, output, session) {
session$onSessionEnded(function(){
file.remove(......)
})
output$Table <- renderDT({
pdfTable <- cbind(input$pdfFile,
pdflink = sprintf('%s',
input$pdfFile$datapath,
input$pdfFile$name,
input$pdfFile$name),
stringsAsFactors = FALSE)
datatable(pdfTable, escape = FALSE)
})
}
I wasn't able to get the downloadButton to appear in the table, but the otherwise I believe the following meets your requirements. The basic idea is to copy the uploaded file to a new tempfile whose location gets saved in a reactiveVal until needed.
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
fileInput('pdfFile',
'Upload PDF',
multiple = TRUE,
accept = c('.pdf')),
downloadButton("download_button", "Download Selected File"),
DTOutput('Table')
)
server <- function(input, output) {
output$Table <- renderDT({
uploaded_df() %>%
select(-temp) %>%
datatable(selection = "single")
})
uploaded_df <- reactiveVal(tibble(name = character(), temp = character()))
observeEvent(input$pdfFile,{
temp_file_location <- tempfile(fileext = ".pdf")
file.copy(input$pdfFile$datapath, temp_file_location)
tibble(name = input$pdfFile$name,
temp = temp_file_location) %>%
bind_rows(uploaded_df(), .) %>%
uploaded_df()
})
output$download_button <- downloadHandler(
filename <- function() {
req(input$Table_rows_selected)
uploaded_df()$name[[input$Table_rows_selected]]
},
content <- function(file) {
file.copy(uploaded_df()$temp[[input$Table_rows_selected]], file)
}
)
}
shinyApp(ui = ui, server = server)
I'm pretty new to shiny (being playing around for about a week). And I'm trying to create an app that takes and input tab-separated text file and perform several exploratory functions. In this case I'm presenting a very simplified version of that app just to highlight what I want to do in a specific case:
Problem:
If you try the app with the sample data (or any data in the same format) you can notice that the app effectively performs the default summary table (if selectInput="summarize", then output$sumfile), but when you try to select "explore", the previous table gets removed from the mainPanel, and outputs the full file (selectInput="explore",then output$gridfile) in the place where it would be as if selectInput="summarize" was still selected.
If you re-select "summarize", excelOutput("sumfile") gets duplicated on the mainPanel.
My goal is simple:
excelOutput("sumfile") when selectInput="summarize" ONLY and
excelOutput("gridfile") when selectInput="explore" ONLY
without placement issues or duplications on the mainPanel
So far I've tried:
inFile=input$df
if(is.null(inFile))
return(NULL)
if(input$show=="summarize")
return(NULL)
or
inFile=input$df
if(is.null(inFile))
return(NULL)
if(input$show=="explore")
return(NULL)
To control what shows up on the mainPanel, but with placement and duplication issues.
sample data:
#Build test data
testdat<-data.frame(W=c(rep("A",3),
rep("B",3),
rep("C",3)),
X=c(letters[1:9]),
Y=c(11:19),
Z=c(letters[1:7],"",NA),
stringsAsFactors = FALSE)
#Export test data
write.table(testdat,
"your/path/file.txt",
row.names = FALSE,
sep = "\t",
quote = FALSE,
na="")
shiny app (app.R):
library(shiny)
library(excelR)
#function to summarize tables
Pivot<-function(df){
cclass<-as.character(sapply(df,
class))
df.1<-apply(df,
2,
function(x) unlist(list(nrows = as.numeric(NROW(x)),
nrows.unique = length(unique(x))-(sum(is.na(x))+length(which(x==""))),
nrows.empty = (sum(is.na(x))+length(which(x==""))))))
df.2<-data.frame(df.1,
stringsAsFactors = FALSE)
df.3<-data.frame(t(df.2),
stringsAsFactors = FALSE)
df.3$col.class<-cclass
df.3$col.name<-row.names(df.3)
row.names(df.3)<-NULL
df.3<-df.3[c(5,4,1,2,3)]
return(df.3)
}
ui <- fluidPage(
ui <- fluidPage(titlePanel(title=h1("Summary generator",
align="center")),
sidebarLayout(
sidebarPanel(
h3("Loading panel",
align="center"),
fileInput("df",
"Choose file (format: file.txt)",
accept = c("plain/text",
".txt")),
selectInput("show",
"Choose what to do with file",
choices=c("summarize","explore")),
p("**'summarize' will output a summary of the selected table"),
p("**'explore' will output the full selected editable table"),
tags$hr()
),
mainPanel(
excelOutput("gridfile"),
excelOutput("sumfile")
))))
server <- function(input, output) {
dat<-reactive({
fp<-input$df$datapath
read.delim(fp,
quote="",
na.strings="\"\"",
stringsAsFactors=FALSE)
})
#get summary
output$sumfile<-renderExcel({
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="explore") #if selectInput = "explore" return nothing
return(NULL)
dat.1<-data.frame(dat())
dat.2<-Pivot(dat.1)
excelTable(dat.2,
defaultColWidth = 100,
search = TRUE)
})
#get full file
output$gridfile<-renderExcel({
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="summarize") #if selectInput = "summarize" return nothing
return(NULL)
dat.1<-data.frame(dat())
excelTable(dat.1,
defaultColWidth = 100,
search = TRUE)
})
}
shinyApp(ui = ui, server = server)
One way to do what you want is to use observeEvent for your inputs input$show and input$df and renderExcel based on your selection of `input$show. Here is an updated version for your code:
library(shiny)
library(excelR)
#function to summarize tables
Pivot<-function(df){
cclass<-as.character(sapply(df,
class))
df.1<-apply(df,
2,
function(x) unlist(list(nrows = as.numeric(NROW(x)),
nrows.unique = length(unique(x))-(sum(is.na(x))+length(which(x==""))),
nrows.empty = (sum(is.na(x))+length(which(x==""))))))
df.2<-data.frame(df.1,
stringsAsFactors = FALSE)
df.3<-data.frame(t(df.2),
stringsAsFactors = FALSE)
df.3$col.class<-cclass
df.3$col.name<-row.names(df.3)
row.names(df.3)<-NULL
df.3<-df.3[c(5,4,1,2,3)]
return(df.3)
}
ui <- fluidPage(
ui <- fluidPage(titlePanel(title=h1("Summary generator",
align="center")),
sidebarLayout(
sidebarPanel(
h3("Loading panel",
align="center"),
fileInput("df",
"Choose file (format: file.txt)",
accept = c("plain/text",
".txt")),
selectInput("show",
"Choose what to do with file",
choices=c("summarize","explore")),
p("**'summarize' will output a summary of the selected table"),
p("**'explore' will output the full selected editable table"),
tags$hr()
),
mainPanel(
excelOutput("gridfile"),
excelOutput("sumfile")
))))
server <- function(input, output) {
dat<-reactive({
fp<-input$df$datapath
read.delim(fp,
quote="",
na.strings="\"\"",
stringsAsFactors=FALSE)
})
observeEvent({
input$show
input$df
}, {
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="explore") {
output$gridfile<-renderExcel({
dat.1<-data.frame(dat())
excelTable(dat.1,
defaultColWidth = 100,
search = TRUE)
})
}
if(input$show=="summarize") {
output$sumfile<-renderExcel({
dat.1<-data.frame(dat())
dat.2<-Pivot(dat.1)
excelTable(dat.2,
defaultColWidth = 100,
search = TRUE)
})
}
})
}
shinyApp(ui = ui, server = server)
Hope it helps!