I want to be able to upload multiple images with file input and display the single image selected in the UI
ui.R
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file","Upload the file", multiple = TRUE), # fileinput() function is used to get the file upload contorl option
uiOutput("selectfile")
),
mainPanel(
uiOutput('images')
)
)
)
server.R
server <- function(input,output) {
## Side bar select input widget coming through renderUI()
# Following code displays the select input widget with the list of file loaded by the user
output$selectfile <- renderUI({
if(is.null(input$file)) {return()}
list(hr(),
helpText("Select the files for which you need to see data and summary stats"),
selectInput("Select", "Select", choices=input$file$name)
)
})
output$images <- renderImage({
if(is.null(input$file)) {return(NULL)}
for (i in 1:nrow(input$file))
{
if(input$file$name[i] == input$Select){
list(src=input$file$datapath[i],
alt= "error")
print(input$file$name[i])
print(input$file$datapath[i])
}
}
})
}
With this solution, the prints of the datapath and the name shows me the right answer but i keep getting the same error after trying to render the image: "Warning: Error in basename: a character vector argument expected".
Here is a solution using base64 encoding.
library(shiny)
library(base64enc)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file", "Upload the file", multiple = TRUE),
uiOutput("selectfile")
),
mainPanel(
uiOutput('image')
)
)
)
server <- function(input,output) {
output$selectfile <- renderUI({
req(input$file)
list(hr(),
helpText("Select the files for which you need to see data and summary stats"),
selectInput("Select", "Select", choices=input$file$name)
)
})
output$image <- renderUI({
req(input$Select)
i <- which(input$file$name == input$Select)
if(length(i)){
base64 <- dataURI(file = input$file$datapath[i], mime = input$file$type[i])
tags$img(src = base64, alt= "error")
}
})
}
shinyApp(ui, server)
Related
I have the below app that can view one pdf after import. However I want to be able to import multiple PDFs (this can already be done), and click the Next PDF actionButton to view the next PDF. All the way till the last imported PDF, how can I do that?
If the below code is not working to view one pdf, please ensure you have a www folder in the same directory of your app.R.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Testing File upload"),
sidebarLayout(
sidebarPanel(
fileInput('file_input', 'upload file ( . pdf format only)',
accept = c('.pdf'),multiple = T),
actionButton("next_pdf", "Next PDF")
),
mainPanel(
uiOutput("pdfview")
)
)
))
server <- shinyServer(function(input, output) {
observe({
req(input$file_input)
file.copy(input$file_input$datapath,"www", overwrite = T)
output$pdfview <- renderUI({
tags$iframe(style="height:1200px; width:100%", src="0.pdf")
})
})
})
shinyApp(ui = ui, server = server)
I have worked out a solution! I use reactiveVal for variable x, and each time the actionButton is clicked, x will increase 1. On that basis, I can view the PDF one by one by specifying datapath[]. It can be very useful for people with multiple pdf files.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Testing File upload"),
sidebarLayout(
sidebarPanel(
fileInput('file_input', 'upload file ( . pdf format only)',
accept = c('.pdf'),multiple = T),
tableOutput("files"),
actionButton("next_pdf", "Next PDF"),
textOutput("testing")
),
mainPanel(
uiOutput("pdfview")
)
)
))
server <- shinyServer(function(input, output) {
x = reactiveVal(1)
output$files <- renderTable({input$file_input})
observeEvent(input$file_input,{
file.copy(input$file_input$datapath[1],"www", overwrite = T)
output$pdfview <- renderUI({
tags$iframe(style="height:1200px; width:100%", src="0.pdf")
})
})
observeEvent(input$next_pdf,{
x(x()+1)
file.rename(input$file_input$datapath[x()], "0.pdf")
file.copy("0.pdf","www", overwrite = T)
output$pdfview <- renderUI({
tags$iframe(style="height:1200px; width:100%", src="0.pdf")
})
output$testing = renderText(x())
})
})
shinyApp(ui = ui, server = server)
Here is an example. What I wanted is to display the example data if user click Show example or will display the data uploaded by user.
df<-data.frame(x=rnorm(9),
y=rnorm(9),
z=rnorm(9))
write.table(df, "test.txt", quote=F)
library(shiny)
ui <- shinyUI(pageWithSidebar(
headerPanel("test"),
sidebarPanel(
actionButton("evReactiveButton", "Show example"),
fileInput("file1", "Upload File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv"))
),
mainPanel(
tableOutput("matrix")
)
))
server <- function(input, output, session) {
#if action button show example
datobj<-reactive({
dat<-matrix(1:100, nrow=10)
return(dat)
})
#if user upload
datobj <- reactive({
req(input$file1)
dat <- read.table(input$file1$datapath)
return(dat)
})
### matrix file
output$matrix <- renderTable({
return(datobj())
})
}
shinyApp(ui=ui,server=server)
Here's one way to do it using observeEvent. I added an actionButton to switch more easily between the example data (not reactive here) and the data the user imports. If you want to remove this button, don't forget to replace also the second observeEvent environment by observe.
df<-data.frame(x=rnorm(9),
y=rnorm(9),
z=rnorm(9))
write.table(df, "test.txt", quote=F)
library(shiny)
ui <- shinyUI(pageWithSidebar(
headerPanel("test"),
sidebarPanel(
actionButton("example", "Show example"),
fileInput("file1", "Upload File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
actionButton("import", "Show data imported")
),
mainPanel(
tableOutput("matrix")
)
))
server <- function(input, output, session) {
#if action button show example
datobj<- mtcars
#if user upload
datobj2 <- reactive({
req(input$file1)
dat <- read.table(input$file1$datapath, sep = " ")
return(dat)
})
### matrix file
observeEvent(input$example, {
output$matrix <- renderTable({
datobj
})
})
observeEvent(input$import, {
if (!is.null(datobj2())){
output$matrix <- renderTable({
datobj2()
})
}
else {}
})
}
shinyApp(ui=ui,server=server)
I am developing a R Shiny application which will rely on a module in hopes that I can re-use the module for uploading and displaying two different data sets. As of now, my code works but I think I could make it a little bit cleaner as I don't think I have gotten the module correct. By that I mean, how do I move this code snippet (below) out of the app_server and into the module server and then use the callModule function for two different datasets. Similarly, I probably need to remove this code: tableOutput("metacontent") from the app ui and have that call in the module ui. See the module ui, module server, app ui, and app server below code snippet. Any suggestions? Thanks!
#code snippet
output$metacontents <- renderTable({
metafile()
})
# Module UI
mod_dataInput_ui <- function(id, label) {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(
# Input: Select a file ----
fileInput(ns("id"), label,
multiple = FALSE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv",
".tsv")),
# Input: Select separator ----
radioButtons(ns("sep"), "Separator",
choices = c(Comma = ",",
Tab = "\t"),
selected = "\t"))
}
# Module Server
mod_dataInput_server <- function(input, output, session) {
userFile <- reactive({
validate(need(input$id !="", "Please import a data file"))
input$id
})
datafile <- reactive({
utils::read.table(userFile()$datapath,
header = FALSE,
sep = input$sep,
row.names = NULL,
skip = 1,
stringsAsFactors = FALSE)
})
}
#App UI
app_ui <- function() {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# List the first level UI elements here
navbarPage("Tinsel",
tabPanel("Load Data",
sidebarPanel(mod_dataInput_ui("dataInput_ui_meta", tags$div("User META data", tags$br(), "(.csv, .tsv, or .txt file format)")), helpText("Can add help text here"),
# Horizontal line ----
tags$hr(style="border-color: black;"),
mod_dataInput_ui("dataInput_ui_gene", tags$div("User GENETIC data", tags$br(), "(.csv, .tsv, or .txt file format)")),
tags$hr(style="border-color: black;")),
mainPanel(
tabsetPanel(
tabPanel("Meta Data",
tableOutput("metacontents")),
tabPanel("Genetic Data",
tableOutput("genecontents"))
)))
)
)
}
#App server
app_server <- function(input, output, session) {
# List the first level callModules here
metafile <- callModule(mod_dataInput_server, "dataInput_ui_meta")
output$metacontents <- renderTable({
metafile()
})
genefile <- callModule(mod_dataInput_server, "dataInput_ui_gene")
output$genecontents <- renderTable({
genefile()
})
}
As far as I see it, you've gotten the module correctly: you're reusing the UI and server on two different IDs for the data import.
You can optimize what you've done by creating a module for the table part, so writing:
# mod_table.R
mod_table_ui <- function(id, name){
ns <- NS(id)
tabPanel(
name,
tableOutput(ns("metacontents"))
)
}
# Module Server
#' #rdname mod_table
#' #export
#' #keywords internal
mod_table_server <- function(input, output, session, file){
ns <- session$ns
output$metacontents <- renderTable({
file()
})
}
And then in app_ui:
#' #import shiny
app_ui <- function() {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# List the first level UI elements here
navbarPage(
"Tinsel",
tabPanel(
"Load Data",
sidebarPanel(
mod_dataInput_ui(
"dataInput_ui_meta",
tags$div(
"User META data",
tags$br(),
"(.csv, .tsv, or .txt file format)"
)
),
helpText("Can add help text here"),
# Horizontal line ----
tags$hr(style="border-color: black;"),
mod_dataInput_ui(
"dataInput_ui_gene",
tags$div(
"User GENETIC data",
tags$br(),
"(.csv, .tsv, or .txt file format)"
)
),
tags$hr(style="border-color: black;")
),
mainPanel(
tabsetPanel(
mod_table_ui("table_ui_1", "Meta Data"),
mod_table_ui("table_ui_2", "Genetic Data")
)
)
)
)
)
}
And app_server:
app_server <- function(input, output, session) {
# List the first level callModules here
metafile <- callModule(mod_dataInput_server, "dataInput_ui_meta")
callModule(mod_table_server, "table_ui_1", metafile)
genefile <- callModule(mod_dataInput_server, "dataInput_ui_gene")
callModule(mod_table_server, "table_ui_2", genefile)
}
Let me know if that answers your question.
This is how I would do it.
library(shiny)
library(ggplot2)
#ui.R
ui <- fluidPage(
titlePanel("My shiny app"), sidebarLayout(
sidebarPanel(
helpText("This app shows how a user can upload a csv file. Then, plot the data.
Any file can be uploaded but analysis is only available
if the data is in same format as the sample file, downloadable below
"),
a("Data to be plotted", href="https://www.dropbox.com/s/t3q2eayogbe0bgl/shiny_data.csv?dl=0"),
tags$hr(),
fileInput("file","Upload the file"),
h5(helpText("Select the read.table parameters below")),
checkboxInput(inputId = 'header', label = 'Header', value = TRUE),
checkboxInput(inputId = "stringAsFactors", "stringAsFactors", FALSE),
br(),
radioButtons(inputId = 'sep', label = 'Separator', choices = c(Comma=',',Semicolon=';',Tab='\t', Space=''), selected = ',')
),
mainPanel(
uiOutput("tb"),
plotOutput("line")
)
)
)
#server.R
server <- function(input,output){
data <- reactive({
file1 <- input$file
if(is.null(file1)){return()}
read.table(file=file1$datapath, sep=input$sep, header = input$header, stringsAsFactors = input$stringAsFactors)})
output$filedf <- renderTable({
if(is.null(data())){return ()}
input$file
})
output$sum <- renderTable({
if(is.null(data())){return ()}
summary(data())
})
output$table <- renderTable({
if(is.null(data())){return ()}
data()
})
output$line <- renderPlot({
if (is.null(data())) { return() }
print(ggplot(data(), aes(x=date, y=aa)) + geom_line()+ facet_wrap(~station)) })
output$tb <- renderUI({if(is.null(data()))
h5()
else
tabsetPanel(tabPanel("About file", tableOutput("filedf")),tabPanel("Data", tableOutput("table")),tabPanel("Summary", tableOutput("sum")))
})
}
shinyApp(ui = ui, server = server)
I am building an app in shiny (R). At the beginning the user can upload a file to use (I am doing a sort data analysis). My goal is to be able to use files without knowing how many columns this file has, and how the data exactly looks like.
So now I have to select the columns by number, and I made a small preview app for this to select columns and then display them next to the original:
library(shiny)
ui <-fluidPage(
headerPanel("Select data"),
sidebarLayout(
sidebarPanel(
fileInput("uploadFile", "XLSX file"),
textInput('vec1', 'Choose training columns', "3,4"),
actionButton("choose","choose data")
),
mainPanel(
fluidRow(
column(6,tableOutput("data_raw")),
column(6,tableOutput("data_selected"))
)
)
)
)
server <- function(input, output) {
output$data_raw <- renderTable({
inFile <- input$uploadFile
if (is.null(inFile))
return(NULL)
data_raw <<-read.xlsx(inFile$datapath, 1)
})
observe({
if(input$choose>0){
selectvec <- as.numeric(unlist(strsplit(input$vec1,",")))
output$data_selected <- renderTable(
data_selected<- data_raw[,selectvec]
)
}
})
}
shinyApp(ui,server)
Now I would like to be able to select the columns to use on basis of their header.
It feels unnatural: changing the app while running.. but in a reactive environment.. why not?
QUESTION: How can I change the UI while it is allready running, with values originating from the input?
kind regards,
Pieter
To make me feel not as dirty for answering this...I didn't debug or handle reactives properly. But here ya go. You need to respond to the file that is uploaded on the server side, extract the column names, and append thosed to the choices in a select input that then passes down to the table function as a column filter.
upload_app <- function(){
library(shiny)
ui <- bootstrapPage(
tags$div(class = "container",
column(3,
fluidRow(
fileInput(inputId = 'user_data',
label = 'Upload Data (csv)',
multiple = FALSE,
accept = c(
'text/csv',
'text/comma-separated-values',
'text/tab-separated-values',
'text/plain',
'.csv',
'.tsv'
))
),
fluidRow(
uiOutput('column_vars')
)
),
column(9,
tableOutput('filtered_table'))
)
)
server <- function(session, input, output){
var_table <- reactive({
var_data <- input$user_data
read.csv(var_data$datapath, header = TRUE,sep = ",", quote = '')
})
output$column_vars <- renderUI({
if(!is.null(var_table())){
selectInput(inputId = 'cols',
choices = colnames(var_table()),
multiple = T,
label = "Choose Columns")
}
})
output$filtered_table <- renderTable({
if(!is.null(var_table())){
if(length(input$cols)>0){
get_these <- input$cols
new_table <- var_table()[,c(get_these)]
}else {
new_table <- var_table()
}
}else {
new_table <- data.frame(data = 'Waiting')
}
return(new_table)
})
}
shinyApp(ui, server)
}
I have a shiny app, that reads in files that are uploaded by a user
Different people have different versions of excel. So if a user is using excel 2007 or excel 2010 we use one section of code. If they upload it in excel 2003 we use a different library to read the file. The user specifies in the form which version of excel they have
The function to do this is below
get_data <- function(strFilePath, storageType) {
if (is.null(strFilePath))
return(NULL)
if (storagetType == 'xls2010' || storagetType == 'xls2007'){
df <- openxlsx:read.xlsx(strFilePath,sheet = 1)
}
else if (storagetType == 'xls2003'){
df <- XLConnect:readWorksheetFromFile(strFilePath)
}
return(df)
}
To implement this in shiny, i have two widgets. A fileInput and a selectInput. The user selects which version of excel they are running and then selects the file which then is read in by the function get_data. I suspect its because I'm not utilizing the reactivity correctly. When i run the app and upload the file i get the error message
Error: object 'storagetType' not found
# Global.R
storage_types <- c(
"Excel 2010" = "xls2010",
"Excel 2007" = "xls2007",
"Excel 2003" = "xls2003"
)
# UI.R
ui <- shinyUI(fluidPage(
navbarPage("Navbar!",
# Tab contains all the information to upload a file
tabPanel("Upload Data",
# Side Panel with Options
fluidRow(
column(4, wellPanel(
id = "leftPanel",
div(
id = "Header",
h3("Options", align = "center"),
tags$hr()
),
div(
selectInput("xlsversion", "2. Select your Excel version", storage_types),
fileInput(inputId = 'file1',label = '3. Choose An Excel File'),
)
)))))))
# Server.R
server <- shinyServer(
function(input, output) {
# When the Browser to the file location gets updated
upload_data <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
get_data(inFile$datapath, input$xlsversion)
})
})
You not need selectInput simply parse name of file.
Also some typo fixed
library(shiny)
get_data <- function(strFilePath, storageType) {
if (is.null(strFilePath))
return(NULL)
file_ext=substring(storageType,nchar(storageType)-3)
if (file_ext == 'xlsx' ){
df <- openxlsx::read.xlsx(strFilePath,sheet = 1)
}
else if (file_ext == '.xls'){
df <- XLConnect::readWorksheetFromFile(strFilePath,sheet=1)
} else{
return(data.frame("Bad file format"))
}
return(df)
}
# UI.R
ui <- shinyUI(fluidPage(
navbarPage("Navbar!",
# Tab contains all the information to upload a file
tabPanel("Upload Data",
# Side Panel with Options
fluidRow(
column(4, wellPanel(
id = "leftPanel",
div(
id = "Header",
h3("Options", align = "center"),
tags$hr()
),
div(
fileInput(inputId = 'file1',label = '3. Choose An Excel File')
)
))),
dataTableOutput("result")))))
# Server.R
server <- function(input, output) {
# When the Browser to the file location gets updated
upload_data <- reactive({
if (is.null(input$file1))
return(NULL)
get_data(input$file1$datapath, input$file1$name)
})
output$result=renderDataTable({
upload_data()
})
}
shinyApp(ui,server)