I have created a small Shiny Application using Plotly heatmap and intend to use withSpinner to plot Heat Map depending on user input. Currently i have two issues.
a.) WithSpinner appears only for the first time when heat map is generated. It doesn't appear if the User input is changed and replotting is done.
b.) On change of User input, the previous heatmap is shown instead of spinner and it refreshes after sometime. I intend to use spinner instead of showing old plot during redrawing of heatmap.
library(shiny)
library(shinydashboard)
library(shinycssloaders)
library(shinyjs)
library(plotly)
ui <- shinydashboard::dashboardPage(
# Dashboard header
shinydashboard::dashboardHeader(),
# Dashboard sidebar
shinydashboard::dashboardSidebar(disable = TRUE),
# Dashboard body
shinydashboard::dashboardBody(
id = "myBody",
# Tab items
shinydashboard::tabItem(tabName = "visual",
fluidRow(
shinydashboard::tabBox(id="tabBix1",
shiny::tabPanel(
"Parameters & Settings",
value = "paramsetting",
sliderInput("obs", "Number of observations:",
min = 0, max = 1000, value = 500
)),
shiny::tabPanel(
"PlotOutput",
value = "Ops",
withSpinner(plotly::plotlyOutput("plotNewExp"))
)
)
))
)
)
server <- function(input, output, session) {
p <- NULL
observeEvent(input$obs,{
p <- NULL
m <- matrix(rnorm(input$obs), nrow = 3, ncol = 3)
output$plotNewExp <- plotly::renderPlotly({
p <- plot_ly(
x = c("a", "b", "c"), y = c("d", "e", "f"),
z = m, type = "heatmap"
)
})
})
}
shinyApp(ui=ui,server=server)
If you try this you will see that the spinner is working but it is fast. So you probably don't have the time to see it when you switch from one tab to the other.
ui <- shinydashboard::dashboardPage(
# Dashboard header
shinydashboard::dashboardHeader(),
# Dashboard sidebar
shinydashboard::dashboardSidebar(disable = TRUE),
# Dashboard body
shinydashboard::dashboardBody(
id = "myBody",
# Tab items
shinydashboard::tabItem(tabName = "visual",
fluidRow(
shinydashboard::tabBox(
tabPanel(
"Parameters & Settings",
value = "paramsetting",
sliderInput("obs", "Number of observations:",
min = 0, max = 1000, value = 500
),
withSpinner(plotly::plotlyOutput("plotNewExp")))
# tabPanel(
# "PlotOutput",
# withSpinner(plotly::plotlyOutput("plotNewExp"))
# )
)
))
)
)
server <- function(input, output, session) {
# p <- NULL
# observeEvent(input$obs,{
# p <- NULL
m <- reactive({matrix(rnorm(input$obs), nrow = 3, ncol = 3)})
output$plotNewExp <- renderPlotly({
p <- plot_ly(
x = c("a", "b", "c"), y = c("d", "e", "f"),
z = m(), type = "heatmap"
)
# })
})
}
shinyApp(ui=ui,server=server)
or if you add a delay, you will see it is working.
library(shinyjs)
ui <- shinydashboard::dashboardPage(
# Dashboard header
shinydashboard::dashboardHeader(),
# Dashboard sidebar
shinydashboard::dashboardSidebar(disable = TRUE),
# Dashboard body
shinydashboard::dashboardBody(
useShinyjs(),
id = "myBody",
# Tab items
shinydashboard::tabItem(tabName = "visual",
fluidRow(
shinydashboard::tabBox(id="tabBix1",
shiny::tabPanel(
"Parameters & Settings",
value = "paramsetting",
sliderInput("obs", "Number of observations:",
min = 0, max = 1000, value = 500
)),
shiny::tabPanel(
"PlotOutput",
value = "Ops",
withSpinner(plotly::plotlyOutput("plotNewExp"))
)
)
))
)
)
server <- function(input, output, session) {
# p <- NULL
Graph <- function() {
p <- NULL
m <- matrix(rnorm(input$obs), nrow = 3, ncol = 3)
output$plotNewExp <- plotly::renderPlotly({
p <- plot_ly(
x = c("a", "b", "c"), y = c("d", "e", "f"),
z = m, type = "heatmap"
)
})
}
observeEvent(input$obs,{
delay(4000, Graph())
})
}
shinyApp(ui=ui,server=server)
Related
I am having trouble getting boxes to align in fluidRow column format when using bslib and theme bootstrap 4 and 5 with navbarPage. It works with version 3. I can't figure out how to make the version 5 (or 4) to return the right layout. Only one column is used for the later versions. I need the boxes to be arranged in rows.
library(shiny)
library(bslib)
library(shinydashboard)
ui <- navbarPage(
theme = bs_theme(version = 4), # Wrong Layout
#theme = bs_theme(version = 3), # Right Layout
title="Dynamic Boxes",
#
navbarMenu("Tab1" ,
tabPanel("Item1",
fluidPage(
fluidRow( column(12,
uiOutput("boxes")
)
)
)
)
)
)
server <- function(input, output) {
output$boxes <- renderUI({
lapply(1:10, function(a) {
x = 1:100
box(title = paste0("box ", a), renderPlot(plot(x = x, y = x^a)))
})
# )
})
}
shinyApp(ui = ui, server = server)
fluidRow() needs to be outside of the lapply function and column() inside.
library(shiny)
library(bslib)
library(shinydashboard)
ui <- navbarPage(
theme = bs_theme(version = 4), # Wrong Layout
#theme = bs_theme(version = 3), # Right Layout
title="Dynamic Boxes",
navbarMenu("Tab1" ,
tabPanel("Item1",
uiOutput("boxes")
)
)
)
server <- function(input, output) {
output$boxes <- renderUI({
fluidRow(
lapply(1:10, function(a) {
x = 1:100
column(6,
box(title = paste0("box ", a),
renderPlot(plot(x = x, y = x^a)))
)
})
)
})
}
shinyApp(ui = ui, server = server)
Below is the shiny application. Is there a way to track how the user interacts with the application, for example, from three inputs there,
what all he selects
Can we capture point 1 in a table
To be very specific, the user selects below combinations, so I need to capture this in a table . Is this possible?
if (interactive()) {
# Classic Iris clustering with Shiny
ui <- fluidPage(
headerPanel("Iris k-means clustering"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "xcol",
label = "X Variable",
choices = names(iris)
),
selectInput(
inputId = "ycol",
label = "Y Variable",
choices = names(iris),
selected = names(iris)[[2]]
),
numericInput(
inputId = "clusters",
label = "Cluster count",
value = 3,
min = 1,
max = 9
)
),
mainPanel(
plotOutput("plot1")
)
)
)
server <- function(input, output, session) {
# classic server logic
selectedData <- reactive({
iris[, c(input$xcol, input$ycol)]
})
clusters <- reactive({
kmeans(selectedData(), input$clusters)
})
output$plot1 <- renderPlot({
palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
"#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))
par(mar = c(5.1, 4.1, 0, 1))
plot(selectedData(),
col = clusters()$cluster,
pch = 20, cex = 3)
points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
})
}
shinyApp(ui, server)
}
I have tried this in different ways and achieved one task, either add or delete., but i couldn't get complete solution in one, i might be missing some small concept somewhere.. I am adding the code , please help me complete my basic app.
library(shiny)
library(DT)
x<- data.frame(v1 = NA,
v2 = NA
),
ui = shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
textInput("v1","v1","a"),
numericInput("V2","V2","1"),
# Row selection
numericInput(inputId = "row.selection", label = "Select row to be
deleted", min = 1, max = 100, value = "")
# Add button
actionButton(inputId = "add.button", label = "Add", icon =
icon("plus")),
# Delete button
actionButton(inputId = "delete.button", label = "Delete", icon =
icon("minus")),
),
mainPanel(
dataTableOutput('table')
)
)
)
),
Server side code
server = function(input, output, session) {
values <- reactiveValues()
values$df <- x
newEntry <- observe({
cat("newEntry\n")
if(input$add.button > 0) {
newRow <- data.frame(input$v1, input$v2)
isolate(values$df <- rbind(values$df,newRow))
}
})
deleteEntry <- observe({
cat("deleteEntry\n")
if(input$delete.button > 0) {
if(is.na(isolate(input$row.selection))){
values$df <- isolate(values$df[-nrow(values$df), ])
} else {
values$df <- isolate(values$df[-input$row.selection, ])
}
}
})
output$table = renderDataTable({
values$df
})
}
Try to use observeEvent rather than obser with actionbutton
and also, you have uppercase and lowercase issue with input$v2 (should be input$V2)
Try this modified code:
library(shiny)
library(DT)
x<- data.frame(v1 = NA,
v2 = NA
)
ui = shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
textInput("v1","v1","a"),
numericInput("V2","V2","1"),
# Row selection
numericInput(inputId = "row.selection", label = "Select row to be
deleted", min = 1, max = 100, value = ""),
# Add button
actionButton(inputId = "add.button", label = "Add", icon =
icon("plus")),
# Delete button
actionButton(inputId = "delete.button", label = "Delete", icon =
icon("minus"))
),
mainPanel(
dataTableOutput('table')
)
)
)
)
server = function(input, output, session) {
values <- reactiveValues()
values$df <- x
observeEvent(input$add.button,{
cat("addEntry\n")
print(input$v1)
print(input$V2)
newRow <- data.frame(input$v1, input$V2)
colnames(newRow)<-colnames(values$df)
values$df <- rbind(values$df,newRow)
print(nrow(values$df))
})
observeEvent(input$delete.button,{
cat("deleteEntry\n")
if(is.na(input$row.selection)){
values$df <- values$df[-nrow(values$df), ]
} else {
values$df <- values$df[-input$row.selection, ]
}
})
output$table = renderDataTable({
values$df
})
}
shinyApp(ui,server)
Just run all the code above, and it should work properly.
I am trying to get access to colnames(input$file1) for a selectInput drop down menu in a UI. In addition, I would like the default column that is selected to be the first numeric column. I'm not exactly how to accomplish this. It is beyond my shiny skill set. If anyone has any suggestions I'd really appreciate it.
Thanks,
gwynn
runApp(
list(
ui = fluidPage(
titlePanel("Use readxl"),
tabsetPanel(
tabPanel("Upload Data",
sidebarPanel(
helpText("Input an excel file here. We assume the data are in the
first sheet with the first row being headers."),
br(),
fileInput("file1", "Choose Excel File",
accept = c(
"xls",
"xlsx")
),
br()
)),
## This is where I get completely lost.
tabPanel("General Inputs",
selectInput("select", label = h3("Select column"),
choices = colnames(M()[[1]]), selected = M()[[2]][1])
)
),
server = function(input, output){
M = reactive({
M = taglist()
inFile <- input$file1
if(is.null(inFile))
return(NULL)
file.rename(inFile$datapath,
paste(inFile$datapath, ".xlsx", sep=""))
D = read_excel(paste(inFile$datapath, ".xlsx", sep=""), 1)
D = as.data.frame(D)
## Upating file names
colnames(D) <- labs <- gsub("\r\n"," ", colnames(D))
nums = labs[which(sapply(D, is.numeric) == TRUE)]
M[[1]] = D
M[[2]] = nums
})
}
)
)
)
I asked this a few weeks ago and noone answered, in the meantime I fixed it:
runApp(
list(
ui = fluidPage(
titlePanel("Use readxl"),
tabsetPanel(
tabPanel("Upload Data",
sidebarPanel(
helpText("Input an excel file here. We assume the data are in the
first sheet with the first row being headers."),
br(),
fileInput("file1", "Choose Excel File",
accept = c(
"xls",
"xlsx")
),
br(),
actionButton("UI", "Add my data")
)),
tabPanel("General Inputs",
uiOutput("VarsInput")
)
)),
server = function(input, output){
M = reactive({
inFile <- input$file1
M = tagList()
if(is.null(inFile))
return(NULL)
file.rename(inFile$datapath,
paste(inFile$datapath, ".xlsx", sep=""))
D = read_excel(paste(inFile$datapath, ".xlsx", sep=""), 1)
D = as.data.frame(D)
## Upating file names
colnames(D) <- labs <- gsub("\r\n"," ", colnames(D))
nums = labs[which(sapply(D, is.numeric) == TRUE)]
M[[1]] = D
M[[2]] = nums
M
})
output$VarsInput <- renderUI({
selectInput("Choose a variable", "Variable to randomize:",
choices = colnames(M()[[1]]), selected = M()[[2]][1])
})
}
)
)
Basically, I forgot to tell the reactive what to spit out, then I got lost b/c computers are stupid.
This app is creating a vector of standardised names which I create given some user input (number of channels and replicates). An example of the standard names given the number of channels = 4 and and replicates = 1 is as follows:
c("rep1_C0","rep1_C1","rep1_C2","rep1_C3")
I would like to allow the user to replace the value of the selection with their own custom value. For example to change the input "rep1_C0" to "Control_rep1". And then for it to then update the reactive vector in question. Here is my code:
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
),
uiOutput("selectnames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
standardNames <- reactive({
paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
})
output$selectnames <- renderUI({
selectizeInput("selectnames", "Change Names", choices = standardNames(),
options = list(maxOptions = input$reps * input$chans))
})
## output
output$testcols <- renderTable({
standardNames()
})
})
shinyApp(ui = ui, server = server)
Is there some kind of option I can pass in the options sections that will allow this?
With selectizeInput you can set options = list(create = TRUE) to allow the user to add levels to the selection list, but I don't think that is what you want.
Instead, here is code that generates a text input box for each of the standard names, and allows the user to enter a label for them. It uses lapply and sapply to loop over each value and generate/read the inputs.
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
),
uiOutput("setNames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
standardNames <- reactive({
paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
})
output$setNames <- renderUI({
lapply(standardNames(), function(thisName){
textInput(paste0("stdName_", thisName)
, thisName
, thisName)
})
})
labelNames <- reactive({
sapply(standardNames()
, function(thisName){
input[[paste0("stdName_", thisName)]]
})
})
## output
output$testcols <- renderTable({
data.frame(
stdName = standardNames()
, label = labelNames()
)
})
})
shinyApp(ui = ui, server = server)
If you want to hide the list unless the user wants to add labels, you can use a simple checkbox, like this, which hides the label making list until the use checks the box to show it.
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
)
, checkboxInput("customNames", "Customize names?")
, uiOutput("setNames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
standardNames <- reactive({
paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
})
output$setNames <- renderUI({
if(!input$customNames){
return(NULL)
}
lapply(standardNames(), function(thisName){
textInput(paste0("stdName_", thisName)
, thisName
, thisName)
})
})
labelNames <- reactive({
if(!input$customNames){
return(standardNames())
}
sapply(standardNames()
, function(thisName){
input[[paste0("stdName_", thisName)]]
})
})
## output
output$testcols <- renderTable({
data.frame(
stdName = standardNames()
, label = labelNames()
)
})
})
shinyApp(ui = ui, server = server)
Alternatively, if you think the user may want to only change one or a small number of labels, here is a way to allow them to choose which standard name they are applying a label to:
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
)
, uiOutput("setNames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
vals <- reactiveValues(
labelNames = character()
)
standardNames <- reactive({
out <- paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
vals$labelNames = setNames(out, out)
return(out)
})
output$setNames <- renderUI({
list(
h4("Add labels")
, selectInput("nameToChange", "Standard name to label"
, names(vals$labelNames))
, textInput("labelToAdd", "Label to apply")
, actionButton("makeLabel", "Set label")
)
})
observeEvent(input$makeLabel, {
vals$labelNames[input$nameToChange] <- input$labelToAdd
})
## output
output$testcols <- renderTable({
data.frame(
stdName = standardNames()
, label = vals$labelNames
)
})
})
shinyApp(ui = ui, server = server)