I am attempting to build a stopwatch Shiny app.
My end goal is to record "trial" times. Each trial will start when the space bar (key code == 32) is pressed, and will end when the space bar is released. I also want to record the time between my trials, which is time from when the space bar is released to when the space bar is pressed again.
I'd like to get the stopwatch to run continuously when the app is open. However, I want the stopwatch to reset to 0 when I press the space bar while continuing to count up in seconds while holding the space bar, and reset to 0 then start counting up again when I release the space bar.
Currently I am struggling to get my stopwatch (what I called timer()
) to reset to 0 whenever I press spacebar or release it.
Below is the code I have tried.
#install.packages("lubdridate")
#install.packages("shiny")
library(lubridate)
library(shiny)
ui <- fluidPage(hr(),
tags$script('
$(document).on("keydown", function (e) {
Shiny.onInputChange("space_down", e.which == 32);
});'
),
## keyup
tags$script('
$(document).on("keyup", function (e) {
Shiny.onInputChange("space_released", e.which == 32);
});'
),
tags$hr(),
textOutput('stopwatch')
)
server <- function(input, output, session) {
# Initialize the stopwatch, timer starts when shiny app opens.
timer <- reactiveVal(0)
update_interval = 0.01 # each interval increases the timer by one hundrendth of a second
# Output the stopwatch.
output$stopwatch <- renderText({
paste("Time passed: ", seconds_to_period(timer()))
})
# observer that invalidates every second. Increases timer by one update_interval.
observe({
invalidateLater(10, session)
isolate({
timer(round(timer() update_interval,2))
})
})
# observers for Keys == 32 (Spacebar)
observeEvent(input$space_down, {timer(0)})
observeEvent(input$space_released, {timer(0)})
}
shinyApp(ui, server)
Please let me know if I am need to be more specific. Thank you in advance!
CodePudding user response:
Try to us Shiny.setInputValue
instead of Shiny.onInputChange
as shown below.
ui <- fluidPage(hr(),
# tags$script('
# $(document).on("keydown", function (e) {
# Shiny.onInputChange("space_down", e.which == 32);
# });'
# ),
## keyup
# tags$script('
# $(document).on("keyup", function (e) {
# Shiny.onInputChange("space_released", e.which == 32);
# });'
# ),
tags$script(HTML('
document.addEventListener("keydown", function(e) {
Shiny.setInputValue("space_down", e.key, {priority: "event"});
});
')),
tags$script(HTML('
document.addEventListener("keyup", function(e) {
Shiny.setInputValue("space_released", e.key, {priority: "event"});
});
')),
tags$hr(),
textOutput('stopwatch')
)
CodePudding user response:
@YBS has the good point: you have to use Shiny.setInputValue
with the option {priority: "event"}
, otherwise the observer does not react if input$space_down
takes the same value as the previous one.
But this JS code will also trigger an event when any key is pressed (even if the event value is FALSE
, this reacts). So you have to use:
$(document).on("keydown", function(e) {
if(e.which == 32) {
Shiny.setInputValue("space_down", true, {priority: "event"});
}
});