I am trying to make a Shiny app that does the following:
1) Upload a file like this:
X Y
1 3
2 1
3 6
4 4
2) Press a Run button to add 2 to the file values by default, or multiplying by 2 if a box is checked,
3) Making a scatter plot out of the generated values.
My problems are (i) I need to check/uncheck the box and then press again the Run button to display the corresponding plot, and (ii) the checkbox comes back to on every time.
How could I update the plot when I check/uncheck the box without pressing the Run button?
I tried to place the observe() and updateCheckboxInput() function outside the eventReactive() block, but it does simply not work.
My code:
library(shiny)
library(ggplot2)
ui <- fluidPage(
fileInput(
inputId = "input_file",
label = "Choose an input file"
),
actionButton(
inputId = "run_button",
label = "Run"
),
checkboxInput(
inputId = "operation_button",
label = "multiply instead of summing",
value = FALSE
),
plotOutput(
outputId = "my_plot"
)
)
server <- function(input, output, session) {
my_data <- eventReactive(
input$run_button,
{
inFile <- input$input_file
if(is.null(inFile)){
return(NULL)
}
my_in <- read.table(inFile$datapath, header = T, sep = "\t")
my_function <- function(input, operation){
if(operation == "sum"){
input <- input + 2
}else if(operation == "multiply"){
input <- input * 2
}
return(input)
}
button_switch <- ifelse(input$operation_button == FALSE, "sum", "multiply")
observe(
{
updateCheckboxInput(session, "operation_button", "multiply instead of summing", value = button_switch)
}
)
my_in <- my_function(my_in, button_switch)
}
)
output$my_plot <- renderPlot(
{
my_df <- my_data()
ggplot(my_df, aes(x=X, y=Y)) +
geom_point()
}
)
}
shinyApp(ui = ui, server = server)
You could make the eventReactive() dependent on multiple inputs:
my_data <- eventReactive(c(input$run_button, input$operation_button),...)
Related
What I am trying to achieve is to handle dynamically generated UI elements with names based on a counter that is triggered on a button click. This works fine, but I cannot compose the names of these output elements using assign(). Here is a simple example that demonstrates the problem:
library(shiny)
ui <- fluidPage(
actionButton("run_btn", "Run"),
plotOutput('Plot1'),
plotOutput('Plot2'),
plotOutput('Plot3')
)
server <- function(input, output, clientData, session) {
observeEvent(input$run_btn, {
myplot <- renderPlot({
boxplot(1:100)
})
assign(paste('output$Plot', sep = "", input$run_btn), myplot) # DOES NOT WORK!
# output$Plot1 <- myplot # THIS WORKS!
})
}
# Run the application
shinyApp(ui = ui, server = server)
I'm inferring that you want to stack new plots in some fashion, additive, potentially with some cleanup?
Actions:
Press the Run button, it creates a plot of mtcars with a random car highlighted.
Repeat this multiple times, each time a new plot is added, stacked before/above all other plots.
Press the Trim button, and all plots except the most-recent are removed from the UI completely.
library(shiny)
ui <- fluidPage(
actionButton("trim_btn", "Trim"),
actionButton("run_btn", "Run")
)
someplot <- function(nm) {
rand <- sample(nrow(mtcars), size = 1)
plot(disp ~ mpg, data = mtcars, main = paste(nm, "-", rownames(mtcars)[rand]), pch = 16, cex = 1)
points(disp ~ mpg, data = mtcars[rand,,drop=FALSE], pch = 16, cex = 2, col = "red")
}
server <- function(input, output, session) {
idcount <- reactiveVal(0)
observeEvent(input$run_btn, {
thisid <- idcount() + 1
idcount(thisid)
thisid <- paste0("plot", thisid)
insertUI(selector = "#run_btn", where = "afterEnd",
ui = plotOutput(thisid))
output[[thisid]] <- renderPlot({ someplot(thisid) })
})
observeEvent(input$trim_btn, {
curid <- idcount() - 1
if (curid > 0) {
selectors <- paste0("#plot", seq_len(curid))
# this could be improved to only remove existing selectors
for (sel in selectors) removeUI(selector = sel)
}
})
}
# # Run the application
shinyApp(ui = ui, server = server)
I have found the solution in the first answer to this question (checkboxGroupInput - set minimum and maximum number of selections - ticks) does not work as expected. The reproducible example is as follows:
rm(list = ls())
library(shiny)
my_min <- 1
my_max <- 3
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("SelecetedVars", "MyList:",paste0("a",1:5), selected = "a1")
),
mainPanel(textOutput("Selected"))
)
)
server <- function(input, output,session) {
output$Selected <- renderText({
paste(input$SelecetedVars,collapse=",")
})
observe({
if(length(input$SelecetedVars) > my_max)
{
updateCheckboxGroupInput(session, "SelecetedVars", selected= tail(input$SelecetedVars,my_max))
}
if(length(input$SelecetedVars) < my_min)
{
updateCheckboxGroupInput(session, "SelecetedVars", selected= "a1")
}
})
}
shinyApp(ui = ui, server = server)
When selecting checkboxes as you go down the list new selections are added to the tail of the input$SelectedVars vector and thus the tail(input$SelecetedVars,my_max) returns the last three vars the user selected. However as you go back up the list the vars are added to the head of the input$SelectedVars vector so tail(input$SelecetedVars,my_max) continues to return the vars already selected.
My current patch to this is to add a note on my app that says only three vars can be selected at a time. However this relies on the user to understand they have to un-check variables themselves. So for the sake of simplicity I am wondering if there is a way to have the most recent selected var to be appended to the tail of the vector so you can always display the last vars the user selected.
EDIT 2020/12/17: Including new reprex to illustrate infinite cycling produced from #Ben's 2020/12/16 edit. I removed the min vars as well as this wont be used in my case.*
library(shiny)
library(shinyjs)
library(tsibble)
library(tsibbledata)
library(tidyr)
library(plotly)
df <- aus_production # demo data from tsibbledata package
my_max <- 2
vars_list <- c("Beer", "Tobacco", "Bricks", "Cement", "Electricity", "Gas")
ui <- fluidPage(
useShinyjs(),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("SelectedVars", "MyList:",vars_list, selected = "Beer")
),
mainPanel(
plotlyOutput("plot1", height = "40vh"),
textOutput("Selected"))
)
)
server <- function(input, output,session) {
last_checked <- reactiveVal("Business")
output$Selected <- renderText({
paste(input$SelectedVars,collapse=",")
})
observeEvent(input$SelectedVars, {
shinyjs::disable("SelectedVars")
s <- input$SelectedVars
isolate({
if(length(s) > my_max)
{
removed <- last_checked()[1]
} else {
removed <- c(setdiff(last_checked(), s))
}
Sys.sleep(.5)
complete <- c(last_checked(), c(setdiff(s, last_checked())))
last_checked(complete[!complete %in% removed])
updateCheckboxGroupInput(session, "SelectedVars", selected = last_checked())
shinyjs::enable("SelectedVars")
})
}, ignoreInit = TRUE, ignoreNULL = FALSE)
output$plot1 <- renderPlotly({
req(input$SelectedVars)
vars <- input$SelectedVars
df_plot <- df %>%
select(Quarter:Tobacco)
if(length(input$SelectedVars) == 2){
plot_ly(data = df_plot,
type = "scatter",
mode ="lines"
) %>%
add_trace(x = ~Quarter,
y = ~df_plot[[2]]) %>%
add_trace(x = ~Quarter,
y = ~df_plot[[3]])
} else {
plot_ly(df_plot) %>%
add_lines(x = ~Quarter,
y = ~df_plot[[2]])
}
})
}
shinyApp(ui = ui, server = server)
What I'm trying to achieve is to have an initial table to be rendered right as the app is executed. But then, update the table only on executing action.
Here's the example:
library(shiny)
library(data.table)
dt <- data.table(x = c("a", "b"), y = c(0,0))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "inSelect",
label = "Select:",
choices = dt[,unique(x)]),
actionButton(inputId = "trigger",
label = "Trigger",
icon = icon("refresh"))
),
mainPanel(
tableOutput("outTable")
)
)
)
server <- function(input, output){
re <- eventReactive(input$trigger, {
dt[x == input$inSelect, y := y + 1]
})
output$outTable <- renderTable({
re()
})
}
shinyApp(ui, server)
So the issue is that under renderTable() I can put either dt to show initial table or re() to show each update after first press of the "Trigger" button.
Do
re <- eventReactive(input$trigger, {
dt[x == input$inSelect, y := y + 1]
}, ignoreNULL = FALSE)
From ?eventReactive:
Both observeEvent and eventReactive take an ignoreNULL parameter that
affects behavior when the eventExpr evaluates to NULL (or in the
special case of an actionButton, 0).
I am working on a shiny app where I am using radioGroupButtons from ShinyWidgets. So for each button I am trying to switch to different output like table or a plot. How do I link the radio button to the outputs
library(shinyWidgets)
library(shinipsum)
library(htmlwidgets)
ui <- navbarPage(
div(
id = "section1-1",
radioGroupButtons(
inputId = "Id069",
# label = "Choose a graph :",
choices = c(
`<i class='fa fa-bar-chart'></i>` = "bar",
`<i class='fa fa-line-chart'></i>` = "line",
`<i class='fa fa-pie-chart'></i>` = "pie"
),
justified = TRUE
)
)
)
server <- function(input, output, session) {
# observe({
# x <- input$inRadioButtons
#
# # Can also set the label and select items
# updateRadioButtons(session, "inRadioButtons2",
# label = paste("radioButtons label", x),
# choices = x,
# selected = x
# )
# })
output$plot <- renderPlot({
random_ggplot()
})
}
shinyApp(ui, server)
On the server side, you can access the input ID like this:
Value = input$Id069
So, add logic like this to server side (within output$plot):
If (Value == x) {
Plot1()
} else {
Plot2()
}
You might want to look into conditional panels on the UI.
I am making an app that takes input from a slider to create the matching number of input text boxes. However, when I print the values from the input boxes it does not always update.
Example:
Pick 3 on slider input. Put 1,2,3 into the 3 text boxes respectively.Hit submit. Prints number = 1 number = 2 number = 3. When I move the slider to 2 and hit enter, I get number = 1 number = 2 despite no values being in the text input anymore. If I move the slider to 4, I will than get the output number = NA number = NA number = 3 number = NA.
Clearly it is remembering previous input values, but I cannot understand why or how to fix it.
ui.R
shinyUI(fluidPage(
fluidRow(
column(4, wellPanel(
sliderInput("numObs", "Number of observations", 1, 30, 3),
uiOutput("buttons"),
submitButton(text = "Apply Changes", icon = NULL)
)),
column(8,
textOutput("a")
)
)
))
server.R
shinyServer(function(input, output) {
output$buttons <- renderUI({
obs <- input$"numObs";
objs <-list();
for (i in 1:obs ){
objs <- list(objs, numericInput(inputId = paste0("t", i), "Day:", NA),br());
}
objs <- fluidRow(objs);
})
t<- function(){
for(i in 1:input$"numObs"){
if(i ==1){
t <- c(as.numeric(input[[paste0("t",i)]])[1]);
}
else{
t <- c(t,as.numeric(input[[paste0("t",i)]])[1]);
}
}
return(t);
}
output$a<- renderText({
paste("number = ", t());
})
})
I made some changes and introduced a few things to your code. Its better to use actionButton than the submitButton as it is more flexible. If you dont like the style of the actionButton, look into Shiny Themes
rm(list = ls())
library(shiny)
ui =(fluidPage(
fluidRow(
column(4, wellPanel(
sliderInput("numObs", "Number of observations", 1, 30, 3),
uiOutput("buttons"),
actionButton("goButton", "Apply Changes")
)),
column(8,textOutput("a"))
) ))
server = (function(input, output) {
output$buttons <- renderUI({
obs <- input$"numObs";
objs <-list();
for (i in 1:obs ){
objs <- list(objs, numericInput(inputId = paste0("t", i), "Day:", NA),br());
}
objs <- fluidRow(objs);
})
# keep track if the Number of obseervations change
previous <- eventReactive(input$goButton, {
input$numObs
})
t <- eventReactive(input$goButton, {
for(i in 1:input$"numObs"){
if(i ==1){
t <- c(as.numeric(input[[paste0("t",i)]])[1]);
}
else{
t <- c(t,as.numeric(input[[paste0("t",i)]])[1]);
}
}
return(t)
})
output$a<- renderText({
if(previous() != input$numObs){
return()
}
paste("number = ", t());
})
})
runApp(list(ui = ui, server = server))