ggplot2:如何区分点击刷子?

时间:2015-05-29 11:06:23

标签: r ggplot2 shiny

我希望在我的闪亮应用中有一个用户可以点击或选择某些区域的情节,所以我使用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")
    })
  }
))

3 个答案:

答案 0 :(得分: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)

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

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

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

一个可运行的代码:

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)

这里有一个演示:

shiny_brush

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

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