R Shiny key input binding

前端 未结 4 2051
旧时难觅i
旧时难觅i 2020-11-27 15:25

In a Shiny application, is it possible to have a binding that listens to what key a user presses down?

I\'m not too familiar with JavaScript, but I\'m looking for s

相关标签:
4条回答
  • 2020-11-27 16:03

    Building on @jdharrison and @gringer, I wanted a listener that would track not just whether a key has been pressed, but which keys are presently pressed/down.

    This gives me the following:

    library(shiny)
    
    ui = bootstrapPage(
      verbatimTextOutput("results"),
      ## keydown
      tags$script('
        downKeyCount = 0;
        $(document).on("keydown", function (e) {
           Shiny.onInputChange("downKey", downKeyCount++);
           Shiny.onInputChange("downKeyId", e.code);
        });'
      ),
      ## keyup
      tags$script('
        upKeyCount = 0;
        $(document).on("keyup", function (e) {
           Shiny.onInputChange("upKey", upKeyCount++);
           Shiny.onInputChange("upKeyId", e.code);
        });'
      )
    )
    
    server = function(input, output, session){
      keyRecords = reactiveValues()
    
      output$results = renderPrint({ 
        keys = reactiveValuesToList(keyRecords);
        names(keys[unlist(keys)]);
      })
      observeEvent(input$downKey, { keyRecords[[input$downKeyId]] = TRUE });
      observeEvent(input$upKey, { keyRecords[[input$upKeyId]] = FALSE });
    }
    
    shinyApp(ui = ui, server = server)
    

    Key details/changes from above:

    • We use e.code instead of e.which as this gives us a more useful description of what keys are pressed. E.g. "KeyA" instead of 65. However, this means we can not distinguish between capitals and lower case.
    • We use keydown and keyup instead of keypress as keypress appears to timeout. So if you press-and-hold a key, keypress will be active (return TRUE) for 1-2 seconds and then be inactive (return FALSE).
    • We initially used the simple listener by @jdharrison. However we use the key-pressed-count method by @gringer in our code above as otherwise pressing or releasing the same key twice in a row does not respond. But if you want to force users to alternate key presses, then we recommend the simple listener.

    Shiny v1.4, R v3.6.2

    0 讨论(0)
  • 2020-11-27 16:08

    I've been working on an R package {keys} to solve this problem. It's basically a wrapper around the Mousetrap javascript library. With this, observing keys is an easy task:

    library(shiny)
    library(keys)
    
    hotkeys <- c(
      "1", 
      "command+shift+k", 
      "up up down down left right left right b a enter"
    )
    
    ui <- fluidPage(
      useKeys(),
      keysInput("keys", hotkeys)
    )
    
    server <- function(input, output, session) {
      observeEvent(input$keys, {
        print(input$keys)
      })
    }
    
    shinyApp(ui, server)
    

    More information here: https://github.com/r4fun/keys

    0 讨论(0)
  • 2020-11-27 16:10

    If you want this to work for multiple presses, you can create an additional press counter that updates on a key press, bind that value to a different variable, and watch the counter rather than the key code. Here's some example UI code:

      tags$script('
        pressedKeyCount = 0;
        $(document).on("keydown", function (e) {
           Shiny.onInputChange("pressedKey", pressedKeyCount++);
           Shiny.onInputChange("pressedKeyId", e.which);
        });'
      )
    

    And associated server code:

      observeEvent(input$pressedKey, {
        if(input$pressedKeyId >= 49 && input$pressedKeyId <= 57){ # numbers
          values$numClick <- (input$pressedKeyId - 48);
          flipNumber();
        }
        if(input$pressedKeyId >= 37 && input$pressedKeyId <= 40){ # arrow keys
          arrowCode <- input$pressedKeyId - 37;
          xInc <- ((arrowCode+1) %% 2) * (arrowCode - 1);
          yInc <- ((arrowCode) %% 2) * (arrowCode - 2) * -1;
          if(!any(values$click == c(-1,-1))){
            values$click <- (((values$click - 1) + c(xInc, yInc) + 9) %% 9) + 1;
          }
        }
      });
    
    0 讨论(0)
  • 2020-11-27 16:11

    You can add a listener for keypresses. The Shiny.onInputChange can be used to bind the key pressed to a shiny variable:

    library(shiny)
    runApp( list(ui = bootstrapPage(
      verbatimTextOutput("results"),
      tags$script('
        $(document).on("keypress", function (e) {
           Shiny.onInputChange("mydata", e.which);
        });
      ') 
    )
    , server = function(input, output, session) {
    
      output$results = renderPrint({
        input$mydata
      })
    }
    ))
    

    for keydown events you can substitute:

      tags$script('
        $(document).on("keydown", function (e) {
           Shiny.onInputChange("mydata", e.which);
        });
      ') 
    
    0 讨论(0)
提交回复
热议问题