R plotly: how to observe whether a trace is hidden or shown through legend clicks with multiple plots

前端 未结 1 519
陌清茗
陌清茗 2020-12-07 00:08

I am trying to figure out which traces the user hides from a scatter plot by means of deselecting them in the interactive legend of plotly.

I have read this SO post

相关标签:
1条回答
  • 2020-12-07 00:45

    Does it help?

    library(plotly)
    library(shiny)
    library(htmlwidgets)
    
    js <- c(
      "function(el, x){",
      "  el.on('plotly_legendclick', function(evtData) {",
      "    Shiny.setInputValue('trace', evtData.data[evtData.curveNumber].name);",
      "  });",
      "  el.on('plotly_restyle', function(evtData) {",
      "    Shiny.setInputValue('visibility', evtData[0].visible);",
      "  });",
      "}")
    
    ui <- fluidPage(
      plotlyOutput("plot"),
      verbatimTextOutput("legendItem")
    )
    
    server <- function(input, output, session) {
    
      output$plot <- renderPlotly({
        p <- plot_ly()
        for(name in c("drat", "wt", "qsec"))
        {
          p = add_markers(p, x = as.numeric(mtcars$cyl), y = as.numeric(mtcars[[name]]), name = name)
        }
        p %>% onRender(js)
      })
    
      output$legendItem <- renderPrint({
        trace <- input$trace
        ifelse(is.null(trace), 
               "Clicked item will appear here",
               paste0("Clicked: ", trace, 
                      " --- Visibility: ", input$visibility)
        )
      })
    }
    
    shinyApp(ui, server)
    


    EDIT

    There's an issue with the previous solution when one double-clicks on a legend item. Here is a better solution:

    library(plotly)
    library(shiny)
    library(htmlwidgets)
    
    js <- c(
      "function(el, x){",
      "  var d3 = Plotly.d3;",
      "  el.on('plotly_restyle', function(evtData) {",
      "    var out = {};",
      "    d3.select('g.legend').selectAll('.traces').each(function(){",
      "      var trace = d3.select(this)[0][0].__data__[0].trace;",
      "      out[trace.name] = trace.visible;",
      "    });",
      "    Shiny.setInputValue('traces', out);",
      "  });",
      "}")
    
    
    ui <- fluidPage(
      plotlyOutput("plot"),
      verbatimTextOutput("legendItem")
    )
    
    server <- function(input, output, session) {
    
      output$plot <- renderPlotly({
        p <- plot_ly()
        for(name in c("drat", "wt", "qsec"))
        {
          p = add_markers(p, x = as.numeric(mtcars$cyl), y = as.numeric(mtcars[[name]]), name = name)
        }
        p %>% onRender(js)
      })
    
      output$legendItem <- renderPrint({
        input$traces
      })
    }
    
    shinyApp(ui, server)
    


    If you have multiple plots, add the plot id in the legend selector, and use a function to generate the JavaScript code:

    js <- function(i) { 
      c(
      "function(el, x){",
      "  var id = el.getAttribute('id');",
      "  var d3 = Plotly.d3;",
      "  el.on('plotly_restyle', function(evtData) {",
      "    var out = {};",
      "    d3.select('#' + id + ' g.legend').selectAll('.traces').each(function(){",
      "      var trace = d3.select(this)[0][0].__data__[0].trace;",
      "      out[trace.name] = trace.visible;",
      "    });",
      sprintf("    Shiny.setInputValue('traces%d', out);", i),
      "  });",
      "}")
    }
    

    Then do p1 %>% onRender(js(1)), p2 %>% onRender(js(2)), ..., and you get the info about the traces visibility in input$traces1, input$traces2, ....

    Another way is to pass the desired name in the third argument of the JavaScript function, with the help of the data argument of onRender:

    js <- c(
      "function(el, x, inputName){",
      "  var id = el.getAttribute('id');",
      "  var d3 = Plotly.d3;",
      "  el.on('plotly_restyle', function(evtData) {",
      "    var out = {};",
      "    d3.select('#' + id + ' g.legend').selectAll('.traces').each(function(){",
      "      var trace = d3.select(this)[0][0].__data__[0].trace;",
      "      out[trace.name] = trace.visible;",
      "    });",
      "    Shiny.setInputValue(inputName, out);",
      "  });",
      "}")
    
    
    p1 %>% onRender(js, data = "tracesPlot1")
    p2 %>% onRender(js, data = "tracesPlot2")
    
    0 讨论(0)
提交回复
热议问题