R Shiny alternate actionButton - r

I have a simple app that each time click plus1, the value of x increases by 1, and each time click plus10, the value of x increases by 10. But I want plus10 can only be valid after plus1 is clicked. And after plus1 is clicked, plus10 can only be clicked once before being invalid again, which would require another click of plus1 to make it valid. So basically I want the user to click those two buttons alternately, with plus1 needs to be clicked first when the app is launched. Ideally, a warning would be given to the user if the user tried to click plus10 without clicking plus1 first: "Please click plus1 first".
library(shiny)
ui <- fluidPage(
actionButton("plus1", "+ 1"),
actionButton("plus10", "+ 10"),
textOutput("value")
)
server <- function(input, output, session) {
x = reactiveVal(0)
observeEvent(input$plus1,{
x(x()+1) # increment x by 1
})
observeEvent(input$plus10,{
x(x()+10) # increment x by 1
})
output$value = renderText(x())
}
shinyApp(ui, server)

This does what you want, but why do you want this?
library(shiny)
ui <- fluidPage(
actionButton("plus1", "+ 1"),
actionButton("plus10", "+ 10"),
textOutput("value"),
textOutput("warning")
)
server <- function(input, output, session) {
x = reactiveValues(x=0,click1=TRUE)
observeEvent(input$plus1,{
if (x$click1) {
x$x <- x$x+1 # increment x by 1
x$click1 <- !x$click1
} else {
output$warning <- renderText({"Please click plus10 first"})
}
})
observeEvent(input$plus10,{
if (!x$click1) {
x$x <- x$x+10 # increment x by 10
x$click1 <- !x$click1
} else {
output$warning <- renderText({"Please click plus1 first"})
}
})
output$value = renderText(x$x)
}
shinyApp(ui, server)
Let me know if explanation needed

Using shinyjs:
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("plus1", "+ 1"),
disabled(actionButton("plus10", "+ 10")),
textOutput("value")
)
server <- function(input, output, session) {
x = reactiveVal(0)
observeEvent(input$plus1,{
x(x() +1 )
disable('plus1')
enable('plus10')
})
observeEvent(input$plus10, {
x(x() + 10)
enable('plus1')
disable('plus10')
})
output$value <- renderText({x()})
}
shinyApp(ui, server)

You can just another reactive value to track whether or not the user can add 10
library(shiny)
ui <- fluidPage(
actionButton("plus1", "+ 1"),
actionButton("plus10", "+ 10"),
textOutput("value")
)
server <- function(input, output, session) {
x <- reactiveVal(0)
canPlus10 <- reactiveVal(FALSE)
observeEvent(input$plus1,{
x(x()+1) # increment x by 1
canPlus10(TRUE)
})
observeEvent(input$plus10,{
if (canPlus10()) {
x(x()+10) # increment x by 10
} else {
showNotification("Please click +1 first")
}
canPlus10(FALSE)
})
output$value = renderText(x())
}
shinyApp(ui, server)

Related

How to make input$action starts from zero every time I click on Action Buttom in Shiny?

As this link says about Action Button, the input$action return the number of clicks
On the button.
enter link description here
My question is:
How can I, after x-clicks, store the x value in a data frame and make the button starts from zero(actually make input$action starts from zero) and after y-clicks store the y value on this same dataframe and on on and on…?
In other words:
I click 3 times , the input$action sends me the value 3, I store this value, and starts again(somehow, I need to make input$action come back to zero), clicking 8 times the input$action sends me the value 8 I store again e keeps going….
It may be simpler to not use the value of the actionButton directly and just record the number of clicks between events in an external reactiveValues object. This for example has two buttons, one to increase the value and another to add the final value to a vector and reset the counter:
library(shiny)
ui <- fluidPage(
actionButton("action", label = "Action"),
actionButton("store", label = "Store"),
hr(),
fluidRow(column(2, verbatimTextOutput("value")))
)
server <- function(input, output) {
values <- reactiveValues(curr = 0,
out = integer())
observeEvent(input$action, {
values$curr = values$curr + 1
})
observeEvent(input$store, {
values$out <- c(values$out, isolate(values$curr))
values$curr <- 0
})
output$value <- renderPrint({
values$out
})
}
shinyApp(ui, server)
Created on 2022-01-06 by the reprex package (v2.0.1)
If we really need to "reset" the button, we can re-render it. Here is an example app:
library(shiny)
library(dplyr)
ui <- fluidPage(
uiOutput("action"),
actionButton("reset", "Reset"),
verbatimTextOutput("values")
)
server <- function(input, output, session) {
output$action <- renderUI({
actionButton("action_but", "Action")
})
vals <- reactiveVal(tibble(clicks = integer(0)))
counter <- reactiveVal(0)
observe({
counter(input$action_but)
})
observeEvent(input$reset,
{
vals(add_row(vals(), clicks = as.integer(input$action_but)))
output$action <- renderUI({
actionButton("action_but", "Action")
})
counter(input$action_but)
},
ignoreInit = TRUE
)
output$values <- renderPrint({
list(counter(), vals())
})
}
shinyApp(ui, server)
The input$button is read only, so you can't reset the input$button increment without resetting the entire app, which would cause you to lose your saved values. Alternatively, you can keep a copy of the values and reset the count on your copy.
Not sure what you want to do with the reset, but you can still count the number of button presses between your steps, 3, 8 etc. So 8 button presses from 3 would be at input$button == 11.
library(shiny)
ui <- fluidPage(
# Copy the line below to make an action button
actionButton("action", label = "Action"),
hr(),
fluidRow(column(2, verbatimTextOutput("value")),
column(6, verbatimTextOutput("countOutput")))
)
server <- function(input, output) {
# You can access the value of the widget with input$action, e.g.
output$value <- renderPrint({ input$action })
df <- reactiveValues(three = NULL, eight = NULL, thirteen = NULL)
observe({
presses <- c(3, 8, 16)
if(is.null(input$action)){
} else if(input$action == presses[1] & is.null(df$three)) {
df$three = input$action
} else if(input$action == presses[2] & is.null(df$eight)){
df$eight = input$action - presses[1]
} else if(input$action == presses[3] & is.null(df$thirteen)) {
df$thirteen = input$action - presses[2]
}
})
output$countOutput <- renderPrint({
paste(df$three[1], df$eight[1], df$thirteen[1])
})
}
shinyApp(ui, server)

Reactive variable cannot be rendered automatically in R Shiny

I'm writing an online table by Shiny of R language, but something confusing stops me.
What I need is:
Show the full table for the first time.
If you type a number in the input block and submit, just show the top n rows of the table automatically.
What I see is:
DFflt, the variable behind the table, is already changed after submission, but the table on the webpage cannot be reloaded before you refresh the page manually.
What can I do to solve this problem? Thanks!
library(shiny)
library(tidyverse)
DF <- mpg
DFflt <- mpg
ui <- fluidPage(
numericInput("nrow", "Slice the top n rows of DF", value = 0),
actionButton("submit", "Submit"),
actionButton("p_DFflt", "print DFflt in console"),
div('render DFflt:'),
tableOutput("table")
)
server <- function(input, output, session) {
observeEvent(input$submit, {
nrow <- input$nrow
if (nrow > 10) {
showNotification(str_c("Invalid", type="error")) }
else { DFflt <<- DF %>% head(nrow) }
})
observeEvent(input$p_DFflt, { print(DFflt)} )
output$table <- renderTable({DFflt})
}
shinyApp(ui, server)
This should work as you expected:
library(shiny)
library(tidyverse)
DF <- mpg
ui <- fluidPage(
numericInput("nrow", "Slice the top n rows of DF", value = 0),
actionButton("submit", "Submit"),
actionButton("p_DFflt", "print DFflt in console"),
div('render DFflt:'),
tableOutput("table")
)
server <- function(input, output, session) {
n_row <- eventReactive(input$submit, {
input$nrow
})
observe({
if (n_row() > 10) {
showNotification(str_c("Invalid ", type="error"))
}
})
output$table <- renderTable({
if(input$submit == 0){ # when the submit button is not clicked
DF
}else{
if (n_row() >= 0 & n_row() <= 10) {
DF %>% head(n_row())
}
}
})
observeEvent(input$p_DFflt, { print(DF)} )
}
shinyApp(ui, server)

adding a count button to a shiny app that interacts with updateNumericInput

I am building a shiny app and I need a count button to be added that interacts with the numeric input. So I want a numeric input which users can freely use, but that also has a button that users can click that adds 1 to the numeric input (so if a user selects 20, and clicks the button the input becomes 21). I have both working separately, but can't get the interaction to work. The numeric input updates with the add button, but if I change the value with the add numeric input the simply continues at the number of clicks thus far. This is what I now have:
library(shiny)
# https://shiny.rstudio.com/reference/shiny/1.3.2/updateNumericInput.html
# https://gist.github.com/aagarw30/69feeeb7e813788a753b71ef8c0877eb
ui <- shinyUI(
fluidPage(
tags$b("Simple counter using reactiveValues() - An example"),
numericInput("inNumber", "Input number", 0),
actionButton("add1", "+ 1"),
plotOutput("plot")
)
)
server <- function(input, output, session) {
counter <- reactiveValues(countervalue = 0) # Defining & initializing the reactiveValues object
observeEvent(input$add1, {
counter$countervalue <- counter$countervalue + 1 # if the add button is clicked, increment the value by 1 and update it
})
observeEvent(input$add1, {
updateNumericInput(session, "inNumber", value = counter$countervalue )
})
output$plot <- renderPlot({
hist( rnorm(input$add1))
})
}
shinyApp(ui, server)
Maybe this is closer to what you are looking for. You can use one observeEvent for input$add1 button (to increase the counter), and another observeEvent for input$inNumber (set set counter to the numericInput). Your hist could then reference the counter.
server <- function(input, output, session) {
counter <- reactiveValues(countervalue = 0) # Defining & initializing the reactiveValues object
observeEvent(input$add1, {
counter$countervalue <- counter$countervalue + 1 # if the add button is clicked, increment the value by 1 and update it
updateNumericInput(session, "inNumber", value = counter$countervalue)
})
observeEvent(input$inNumber, {
counter$countervalue <- input$inNumber
})
output$plot <- renderPlot({
if (counter$countervalue > 0) {
hist(rnorm(counter$countervalue))
}
})
}
You have to reference the numeric input in the histogramm:
library(shiny)
# https://shiny.rstudio.com/reference/shiny/1.3.2/updateNumericInput.html
# https://gist.github.com/aagarw30/69feeeb7e813788a753b71ef8c0877eb
ui <- shinyUI(
fluidPage(
tags$b("Simple counter using reactiveValues() - An example"),
numericInput("inNumber", "Input number", 0),
actionButton("add1", "+ 1"),
plotOutput("plot")
)
)
server <- function(input, output, session) {
counter <- reactiveValues(countervalue = 0) # Defining & initializing the reactiveValues object
observeEvent(input$add1, {
counter$countervalue <- input$inNumber + 1 # if the add button is clicked, increment the value by 1 and update it
updateNumericInput(session, "inNumber", value = counter$countervalue )
})
output$plot <- renderPlot({
hist( rnorm(input$inNumber))
})
}
shinyApp(ui, server)

Shiny updating whilst function is running?

On clicking "Start Ramp", I am expecting the display to update with the new counter value every time add.to.counter(ticks_per_second) is run in the for loop, but the update only happens at the end of the for loop. Is this because the main ramp function is still executing? How could I make this update such that it updates on each change of the counter value - whilst that main function is still running?
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
sidebarLayout(
sidebarPanel(
actionButton("start", "Start Ramp")
),
mainPanel(
tags$b("Simple counter using reactiveValues() - An example"),
br(),
actionButton("add1", "+ 1"),
actionButton("sub1", "- 1"),
actionButton("reset", "set to 0"),
br(),
textOutput("count")
)
))
server <- function(input, output, session) {
counter <- reactiveValues(countervalue = 0) # Defining & initializing the reactiveValues object
add.to.counter <- function(val) {
counter$countervalue <<- counter$countervalue + val
}
ramp <- function(length_of_ramp, ticks_per_second) {
# length of ramp in seconds
# ticks: divide each second into this number of ticks
ticks <- length_of_ramp*ticks_per_second
for (n in 1:ticks) {
shinyjs::delay(ticks_per_second*1000, add.to.counter(ticks_per_second))
}
}
observeEvent(input$start, { ramp(length_of_ramp = 10, ticks_per_second = 1) }
)
observeEvent(input$add1, {
counter$countervalue <- counter$countervalue + 1 # if the add button is clicked, increment the value by 1 and update it
})
observeEvent(input$sub1, {
counter$countervalue <- counter$countervalue - 1 # if the sub button is clicked, decrement the value by 1 and update it
})
observeEvent(input$reset, {
counter$countervalue <- 0 # if the reset button is clicked, set the counter value to zero
})
output$count <- renderText({
paste("Counter Value is ", counter$countervalue) # print the latest value stored in the reactiveValues object
})
}
# Run the application
shinyApp(ui = ui, server = server)
Your use of Sys.sleep() in
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton("start", "Start Ramp")
),
mainPanel(
tags$b("Simple counter using reactiveValues() - An example"),
br(),
actionButton("add1", "+ 1"),
actionButton("sub1", "- 1"),
actionButton("reset", "set to 0"),
br(),
textOutput("count")
)
))
server <- function(input, output, session) {
add.to.counter <- function(val) {
counter$countervalue <<- counter$countervalue + val
}
ramp <- function(length_of_ramp, ticks_per_second) {
# length of ramp in seconds
# ticks: divide each second into this number of ticks
ticks <- length_of_ramp*ticks_per_second
for (n in 1:ticks) {
add.to.counter(ticks_per_second)
# Sys.sleep(1/ticks_per_second)
}
}
observeEvent(input$start, { ramp(length_of_ramp = 10, ticks_per_second = 1) }
)
counter <- reactiveValues(countervalue = 0) # Defining & initializing the reactiveValues object
observeEvent(input$add1, {
counter$countervalue <- counter$countervalue + 1 # if the add button is clicked, increment the value by 1 and update it
})
observeEvent(input$sub1, {
counter$countervalue <- counter$countervalue - 1 # if the sub button is clicked, decrement the value by 1 and update it
})
observeEvent(input$reset, {
counter$countervalue <- 0 # if the reset button is clicked, set the counter value to zero
})
output$count <- renderText({
paste("Counter Value is ", counter$countervalue) # print the latest value stored in the reactiveValues object
})
}
# Run the application
shinyApp(ui = ui, server = server)
I think the problem is that shinyjs::delay() only delays once even when called within a for loop. I do not know how to make shinyjs::delay() delay each iteration of the for loop. In fact, I suspect that this is not possible, given that shinyjs::delay() only works in interactive sessions or context, whereas the for loop itself is not an interactive or reactive context.
To attain what you want, I've had to have a bit of a rethink on the server code. I've made use of invalidateLater, isolate, and reactiveValue to get the text output to update every second and to calculate the counter every second such that each second, the counter is updated in the text output. Since, this will run forever unless some values are changed, I've used if statements to conditionally print the text output.
Full code below:
library(shiny)
ui <- fluidPage(
useShinyjs(),
sidebarLayout(
sidebarPanel(
actionButton("start", "Start Ramp")
),
mainPanel(
tags$b("Simple counter using reactiveValues() - An example"),
br(),
actionButton("add1", "+ 1"),
actionButton("sub1", "- 1"),
actionButton("reset", "set to 0"),
br(),
uiOutput("count")
)
))
server <- function(input, output, session) {
length_of_ramp = 10; ticks_per_second = 1
ticks <- length_of_ramp*ticks_per_second
counter <- reactiveValues(countervalue = 0, forLoop = ticks, loopCount = Inf, finalCount = 0)
observeEvent(input$start, {
counter$loopCount <- -1
})
observe({
invalidateLater(1000)
counter$loopCount <- isolate(counter$loopCount) + 1
if(counter$loopCount == ticks){counter$finalCount <- counter$loopCount}
print(counter$finalCount)
})
observeEvent(input$add1, {
counter$countervalue <- counter$countervalue + 1 # if the add button is clicked, increment the value by 1 and update it
})
observeEvent(input$sub1, {
counter$countervalue <- counter$countervalue - 1 # if the sub button is clicked, decrement the value by 1 and update it
})
observeEvent(input$reset, {
counter$countervalue <- 0 # if the reset button is clicked, set the counter value to zero
})
output$count <- renderUI({
invalidateLater(millis = 1000, session)
if(counter$loopCount < ticks){
counter$countervalue <- isolate(counter$countervalue) + ticks_per_second
paste("Counter Value is ", isolate(counter$countervalue))
}else if (counter$loopCount == Inf || counter$loopCount == 0 || counter$finalCount == ticks) {
paste("Counter Value is ", isolate(counter$countervalue))
}
})
}
shinyApp(ui, server)

Force shiny to render plot in loop

I have a shiny app that runs a simulation. The goal is to show the user the calculation steps in between as a plot.
How do I force shiny to update the plot?
An MWE would look like this
library(shiny)
server <- function(input, output, session) {
# base plot as a placeholder
output$myplot <- renderPlot(plot(1:1, main = "Placeholder"))
# wait until the button is triggered
observeEvent(input$run, {
print("Do some calculations in 3 steps")
for (i in seq_len(3)) {
print("Do some calculations")
# ...
x <- seq_len(i * 100)
y <- (x + 1)^2 - 1 # this will do for now
print("Plot the data ")
# ISSUE HERE!
# this should render the current step of the simulation, instead it
# renders only after the whole code is run (i.e., after step 3)
output$myplot <- renderPlot(plot(x, y, main = sprintf("Round %i", i), type = "l"))
print("Wait for 1 second for the user to appreciate the plot...")
Sys.sleep(1)
}
})
}
ui <- fluidPage(
actionButton("run", "START"),
plotOutput("myplot")
)
shinyApp(ui = ui, server = server)
The issue is, that shiny runs the code and produces one plot at the end of the simulation, however, I want to get a plot at each simulation step (displayed for at least one second).
Any help/hint is greatly appreciated.
Appendix
I have looked at this post, but replacing the text with a plot/renderPlot doesn't yield the correct results.
You could nest an observer into an observeEvent to make it work. Based on Jeff Allen's code from the SO topic you linked.
Crucial part:
observeEvent(input$run, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
Full code:
library(shiny)
server <- function(input, output, session) {
rv <- reactiveValues(i = 0)
maxIter <- 3
output$myplot <- renderPlot( {
if(rv$i > 0) {
x <- seq_len(rv$i * 100)
y <- (x + 1)^2 - 1 # this will do for now
plot(x, y, main = sprintf("Round %i", rv$i), type = "l")
} else {
plot(1:1, main = "Placeholder")
}
})
observeEvent(input$run, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
}
ui <- fluidPage(
actionButton("run", "START"),
plotOutput("myplot")
)
shinyApp(ui = ui, server = server)

Resources