【问题标题】:ggplot2: how to differentiate click from brush?ggplot2:如何区分点击和画笔?
【发布时间】:2015-08-12 05:24:51
【问题描述】:

我想在我闪亮的应用程序中有一个用户可以单击或选择某些区域的图,因此我使用了plotOutput 的单击和画笔参数。我的问题是,当刷子启动时,点击处理程序也会被调用。我想知道什么时候点击了,我想知道什么时候刷了,但是如果点击是刷子的一部分,那么我想忽略它。

示例:在以下应用程序中,如果您只是刷(单击某处并拖动鼠标),您会收到“点击”消息以及“刷”消息。在这种情况下,我只想获得“刷”消息。

library(shiny)
library(ggplot2)
runApp(shinyApp(
  ui = fluidPage(
    plotOutput("plot", click = "click", brush = "brush")
  ),
  server = function(input, output, session) {
    output$plot <- renderPlot({
      ggplot(mtcars, aes(wt, mpg)) + geom_point()
    })
    observeEvent(input$click, {
      cat("clicked\n")
    })
    observeEvent(input$brush, {
      cat("brushed\n")
    })
  }
))

【问题讨论】:

  • 根据?plotOutput,似乎没有“鼠标按钮向上”消息,您需要正确区分点击和刷牙。一种解决方法是定义一个超时时间(例如 200 毫秒),仅在此超时后才处理单击事件,并在此超时后相应地忽略所有画笔事件。您是否有理由不简单地收听双击事件?
  • 我不想仅仅因为用户体验而使用 dblclick - 它更简单,点击更有意义。也许我只是需要睡觉(凌晨 4 点 30 分),但我不确定你的建议将如何解决我的问题,我想了一会儿,似乎并没有解决我的问题跨度>
  • 如果刷机事件在最后一次点击事件后不到 200 毫秒发生,则由您处理。如果没有,你忽略它。您只在事件发生 200 毫秒后处理点击,您必须为此设置某种计时器。
  • @daattali 你找到解决方案了吗?我很想看看。
  • 不,我得到的官方回答是(至少目前)没有办法做到这一点。我最终使用双击而不是单击

标签: r ggplot2 shiny


【解决方案1】:

我知道这只是一种解决方法,但我唯一的解决方案是撤消激活画笔时最后一次单击的操作;对于用户可以通过单击添加点的绘图,我需要这个。 使用刷子将首先创建点并在释放单击按钮后将其删除。 只是一个缺点:有时您在不注意的情况下单击并进行微刷,在这种情况下,它不会明显地创建点。我的应用:

library(shiny); library(dplyr); library(ggplot2)

ui <- fluidPage(
     fluidRow(

          h1("add by clicking anywhere on the plot"),
          plotOutput("mainplot",
                     click="mainplot_click",
          brush=brushOpts(id="mainplot_brush"))
     ),
     fluidRow(
          actionButton("add", "add"),
          actionButton("reset", "reset")
     )
)

server <- function(input, output, session) {

     vals = reactiveValues(
          keeprows = rep(TRUE, nrow(mtcars)),
          mydata = mtcars
     )

     observeEvent(input$mainplot_click, handlerExpr = {
          my.x = input$mainplot_click$x
          my.y = input$mainplot_click$y
          vals$mydata <- vals$mydata %>% bind_rows(data.frame("wt"=my.x, "mpg"=my.y))
     })

     output$mainplot = renderPlot({
          temp = vals$mydata
          ggplot() +geom_point(data=temp, col="black", fill="white", size=3) + aes(x=wt, y=mpg)
     })

     observeEvent(input$mainplot_brush, handlerExpr = {
           vals$mydata <- vals$mydata %>% head(-1)
     })

     observeEvent(input$reset, handlerExpr = {
          vals$mydata <- mtcars
          vals$keeprows <- rep(TRUE, nrow(mtcars))
     })
     session$onSessionEnded(stopApp)
}

shinyApp(ui, server)

【讨论】:

    【解决方案2】:

    我用一组标志解决了这个问题,认识到拖动事件触发点击(鼠标向下),然后是刷子,然后是第二次点击(实际上是鼠标向上)。我延迟了第一次点击的处理,然后如果我处理画笔则取消第一次点击。如果我正在处理画笔,那么我知道我应该忽略第二次点击。

    single.click.up <- FALSE
    single.click.down <- FALSE
    brush.click <- FALSE
    
    # Brush events trigger plot1_click twice (down and up) with a brush event in between.
    observe({
        input$plot1_click
        if (single.click.down) {
            if (brush.click) {
                single.click.up <<- TRUE
                message("single click down ignored")
            } else {
                message("single click down processed")
            }
            single.click.down <<- FALSE
        } else if (single.click.up) {
            message("single click up, reset brush click")
            brush.click <<- FALSE
            single.click.up <<- FALSE
        } else {
            single.click.down <<- TRUE
            invalidateLater(1000)
            message("new single click. delay.")
        }
    })
    
    observeEvent( input$plot1_brush, {
      brush.click <<- TRUE
      message("Processing brush")
    })
    

    可能有一种更清洁的方法来做到这一点。如果事件丢失或处理缓慢,可能会发生奇怪的事情。但它对我有用。

    【讨论】:

      【解决方案3】:

      我正在为交互式热图开发一个闪亮的应用程序 (https://github.com/jokergoo/InteractiveComplexHeatmap)。在热图上,用户可以单击热图以查看该单元格的值或选择一个区域以捕获子热图。我也不喜欢双击热图,这对用户很不友好。

      以下是我区分点击和刷子的解决方案,可能对您有所帮助:

      一个可运行的代码:

      library(shiny)
      library(grid)
      library(glue)
      library(circlize) # only for random colors
      
      ui = fluidPage(
          plotOutput("plot", width = 600, height = 400,
              click = "click", brush = "brush"),
          tags$script(HTML("
              $('#plot').mousedown(function(e) {
                  var parentOffset = $(this).offset();
                  var relX = e.pageX - parentOffset.left;
                  var relY = e.pageY - parentOffset.top;
                  Shiny.setInputValue('x1', relX);
                  Shiny.setInputValue('y1', relY);
              }).mouseup(function(e) {
                  var parentOffset = $(this).offset();
                  var relX = e.pageX - parentOffset.left;
                  var relY = e.pageY - parentOffset.top;
                  Shiny.setInputValue('x2', relX);
                  Shiny.setInputValue('y2', relY);
                  Shiny.setInputValue('action', Math.random());
              });
          ")),
          fluidRow(
              column(3, htmlOutput("output1")),
              column(3, htmlOutput("output2"))
          )
      )
      
      server = function(input, output, session) {
          output$plot = renderPlot({
              grid.newpage()
              grid.rect()
          })
      
          observeEvent(input$action, {
              if(input$x1 == input$x2 && input$y1 == input$y2) {
                  output$output1 = renderText({
                      isolate(glue("
      <pre style='background-color:{rand_color(1)}'>
      a click:
      x1 = {input$x1}
      y1 = {input$y1}
      x2 = {input$x2}
      y2 = {input$y2}
      input$click$coords_css$x = {input$click$coords_css$x}
      input$click$coords_css$y = {input$click$coords_css$y}</pre>"))
                  })
              } else {
                  output$output2 = renderText({
                      isolate(glue("
      <pre style='background-color:{rand_color(1)}'>
      a brush:
      x1 = {input$x1}
      y1 = {input$y1}
      x2 = {input$x2}
      y2 = {input$y2}
      input$brush$coords_css$xmin = {input$brush$coords_css$xmin}
      input$brush$coords_css$ymin = {input$brush$coords_css$ymin}
      input$brush$coords_css$xmax = {input$brush$coords_css$xmax}
      input$brush$coords_css$ymax = {input$brush$coords_css$ymax}</pre>"))
                  })
              }
          })
      }
      
      shinyApp(ui, server)
      

      这里有一个演示:

      这个想法是在“mousedown”和“mouseup”比较鼠标位置。并且在“mouseup”触发一个反应值(这里是action),以便在server函数中,它可以响应它。 (这是在tags$script(...) 中实现的)。

      如图所示,点击和刷子是独立改变盒子颜色的,刷子不会影响点击的盒子。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-18
        • 2012-04-15
        • 2011-08-27
        • 2016-11-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多