Shiny updating whilst function is running? - r

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)

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)

R Shiny alternate actionButton

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)

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)

Update label of actionButton in shiny

I know that similar question was already answered, however the solution creates a new actionButton with different label upon string-input. What I need is to keep the button(the counter of the button), because when I change the label and create a new button it has a counter of 0(not clicked).
So basically I need something like an update function to just change the label of the actionButton, when it is pressed. You press it once and the label changes.
input$Button <- renderUI({
if(input$Button >= 1) label <- "new label"
else label <- "old label"
actionButton("Button", label = label)
})
Something like this, but without reseting the value of the button(by creating a whole new one).
Thanks!
reactiveValues() can help. Check http://shiny.rstudio.com/articles/reactivity-overview.html for details.
In the following example, I renamed your input$Button to input$click to avoid double usage of the "Button" name.
Since we wrap the label in a renderUI(), input$click initially fires once it is created?!?, that's why I put the label
condition as: if(vars$counter >= 2)
An alternative solution could be to remove the read-only attribute (found here: https://github.com/rstudio/shiny/issues/167)
attr(input, "readonly") <- FALSE
input$click <- 1
For an example
paste the following in your R console:
ui <- bootstrapPage(
uiOutput('Button')
)
server <- function(input, output) {
# store the counter outside your input/button
vars = reactiveValues(counter = 0)
output$Button <- renderUI({
actionButton("click", label = label())
})
# increase the counter
observe({
if(!is.null(input$click)){
input$click
isolate({
vars$counter <- vars$counter + 1
})
}
})
label <- reactive({
if(!is.null(input$click)){
if(vars$counter >= 2) label <- "new label"
else label <- "old label"
}
})
}
# run the app
shinyApp(ui = ui, server = server)
You can use updateActionButton from native shiny package:
ui <- fluidPage(
actionButton('someButton', ""),
h3("Button value:"),
verbatimTextOutput("buttonValue"),
textInput("newLabel", "new Button Label:", value = "some label")
)
server <- function(input, output, session) {
output$buttonValue <- renderPrint({
input$someButton
})
observeEvent(input$newLabel, {
updateActionButton(session, "someButton", label = input$newLabel)
})
}
shinyApp(ui, server)
A few years later some small addition: If you want to switch between button icons, e.g. play / pause button (and switching between labels would be similar) you could do something like this (based on shosaco's answer).
library(shiny)
ui <- fluidPage(
fluidRow(
actionButton("PlayPause", NULL, icon = icon("play"))
)
)
server <- function(input, output, session) {
# whenever the ActionButton is clicked, 1 is added to input$PlayPause
# check if input$PlayPause is even or odd with modulo 2
# (returns the remainder of division by 2)
observeEvent(input$PlayPause, {
if (input$PlayPause %% 2 != 0) {
updateActionButton(session, "PlayPause", NULL, icon = icon("pause"))
} else {
updateActionButton(session, "PlayPause", NULL, icon = icon("play"))
}
})
}
shinyApp(ui = ui, server = server)

Unexpected values from text input in Rshiny

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))

Resources