javascript - 如何使用通用按钮+ selectInput 通过R Shiny 中的javascript更改轨迹n的图例图例状态

标签 javascript r shiny plotly r-plotly

感谢 this 上的答复上一个问题,我开发了一个plotly plotbuttons链接到其 legend ,其中单击 legend更改 reactive variable 的状态包含T/F状态为每个,从而重新渲染 actionbuttons链接到每个trace (数据组)。其他javascript在相反方向执行相同操作,单击 button隐藏/显示 trace + legend plot 中的项目。

现在我希望通过新按钮+选择输入添加另一个交互

简短的问题:
如何点击通用按钮 (#0),
更改 TRUE/'legendonly' 之间的图例状态切换
对于绘图 j (#1) 的迹线 n,
哪里n = input$SelectTrace (#2)
通过使用javascript + onclick对此的争论actionButton

0 actionButton此处称为“SwitchExt”
1 它需要针对特定​​的plotly plot因为我有多个
2个selectInput以痕迹为选择

详细说明:

现在我遇到了以下小问题: 在我的应用程序中,在另一个条件面板中,用户会看到一组具有相同数据的不同图表: - 用户可以选择要突出显示的迹线,它旁边的按钮将根据 T/F 状态列表显示第一个图中的该迹线是否打开/关闭,然后该按钮将显示蓝色/红色,并且是链接到所选轨迹。

场景: 用户选择组n, 点击新的actionButton 'SwitchExt' 这会触发flipYNb_FP1(n) , 操作按钮 YNbuttons... YNb <- YNElement(n) ....

if(values$dYNbs_cyl_el[[YNb]] == TRUE) {

将导致按钮 n 改变状态。

我也可以让它改变values$legenditems[n] ,但在我的绘图代码中,values$legenditems包裹在isolate({ })中每当 javascript 时停止绘图重新渲染链接到legend改变它。

解决方案的概念: 基本上我认为我需要的是而不是改变 values$legenditems直接列出来,就是还有一张javascript链接到 actionButton 'switchExt'通过'onclick'并需要 input$SelectTrace作为输入,然后更改 legendstatus类似于 javascript js1这样做,但然后使用 document.getElementById获取 input$SelectTrace 的值,将其变为numeric ,并更新 legendstatus

应用程序:

 library(plotly)
library(shiny)
library(htmlwidgets)

## js to link buttons to legend
js1 <- c(
  "function toggleLegend(id){",
  "  var ids = id.split('-');",
  "  var plotid = ids[1];",
  "  var plot = document.getElementById(plotid);",
  "  var data = plot.data;",
  "  var v0 = data[index].visible || true;",
  "  var v = v0 == true ? 'legendonly' : true;",
  "  Plotly.restyle(plot, {visible: v}, [index]);",
  "}")

## js code to link legend to buttons
js2 <- 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);",
  "  });",
  "}")



YNElement <-    function(idx){sprintf("YesNo-plot1-%d", idx)}


ui <- fluidPage(
  tags$head(
    tags$script(HTML(js1))
  ),
  fluidRow(
    column(2,
           h5("Keep/Drop choices linked to colorscheme 1"),
           uiOutput('YNbuttons')
    ),
    column(8,
           plotlyOutput("plot1")
    ),
    column(2,
           h5('Switch plot ID and shows the plot remembers the show/hide'),
           actionButton(inputId = 'Switch', label = icon('refresh'), style = "color: #f7ad6e;   background-color: white;  border-color: #f7ad6e;
                        height: 40px; width: 40px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px"),
           br(),
           h5('New Button that does not work on legend.', style = 'font-weight:bold'),
            uiOutput('newswitch'),
           br(),
           selectInput(inputId = 'SelectTrace', label = 'Select Trace', choices = 1:3, selected = 1)
           ), style = "margin-top:150px"
    )

  )

server <- function(input, output, session) {
  values <- reactiveValues(Linked_FP1 = T, NrOfTraces = length(unique(mtcars$cyl)))

  observeEvent(input$SwitchExt, { 
    ## trying to make the user be able to switch the buttons linked to the legend on/off through another button that is in another page. 
    flipYNb_FP1(as.numeric(input$SelectTrace)) 
    req(values$legenditems)  ## don't run if legend items does not exist yet. 
    if(values$dYNbs_cyl_el[as.numeric(input$SelectTrace)]) { values$legenditems[[as.numeric(input$SelectTrace)]] <- T } else {   values$legenditems[[as.numeric(input$SelectTrace)]] <- 'legendonly' } ## problem line is here...... since I need to isolate values$legenditems in the plot code
    ## this does not actually cause the legend to change. If I don't isolate, the plot would re-render due to the change in values$legenditems, which is not what we want
  })




  output$plot1 <- renderPlotly({

    if(values$Linked_FP1) {colors <- c('red', 'blue', 'black') } else {colors <- c('black', 'orange', 'gray')}
    p1 <- plot_ly()
    p1 <-  add_trace(p1, data = mtcars, x = ~disp, y = ~mpg, type = 'scatter', mode = 'markers', color = ~as.factor(cyl), colors = colors)
    p1 <- layout(p1, title = 'mtcars group by cyl with switching colors')
    p1 <- plotly_build(p1)

   isolate({ if(values$Linked_FP1) { for(i in seq_along(p1$x$data)){   ## causes the plot to render with previous hide/show selection 
      p1$x$data[[i]]$visible <- values$legenditems[[p1$x$data[[i]]$name]]}  
    } }) ##This block is isolated because otherwise the plot will re-render when the user clicks 1 of the three buttons

    p1 %>% onRender(js2, data = "tracesPlot1")  ## add the javacode to extract the legend status
    })


  observeEvent(input$Switch, { values$Linked_FP1 <- !values$Linked_FP1    })  ## disable the link in my real app, in this dummy app it switches to plot with different id and colors that is not interactive

  observeEvent(values$NrOfTraces, { 
    values$dYNbs_cyl_el <- rep(T,values$NrOfTraces) ## the list of Yes/No status of groups, from which the 3 buttons on the left are build blue or red
    names(values$dYNbs_cyl_el) <- sapply(1:values$NrOfTraces, function(x) {YNElement(x)}) ## add names to that list
    values$legenditems <- rep(T, values$NrOfTraces)  ## make the legenditem list so that the app doesn't crash when user clicks switchExt without first clicking on legend items
    names(values$legenditems) <- sort(unique(mtcars$cyl)) ## add names to that list

  })


    output$newswitch <- renderUI({ 
    req(input$SelectTrace)
      print(input$SelectTrace)
      if(values$dYNbs_cyl_el[as.numeric(input$SelectTrace)]) { 
    actionButton(inputId = 'SwitchExt', label = icon('refresh'), style = "color: #339fff;   background-color: white;  border-color: #339fff;
                        height: 40px; width: 40px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                 onclick = "toggleLegend(this.id);"
                 )}
    else { actionButton(inputId = 'SwitchExt', label = icon('refresh'), style = "color: #f7ad6e;   background-color: white;  border-color: #f7ad6e;
                        height: 40px; width: 40px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                   onclick = "toggleLegend(this.id);"
                   )}
    })


  observeEvent(input$tracesPlot1, {
    if(values$Linked_FP1) {
    listTraces <- input$tracesPlot1
    values$legenditems <- listTraces  ## store the list of show/hide for when the plot re-renders here
    listTracesTF <- gsub('legendonly', FALSE, listTraces)
    listTracesTF <- as.logical(listTracesTF)
    lapply(1:values$NrOfTraces, function(el) {
      if(el <= length(listTracesTF)) {
        YNb <- YNElement(el)
        if(values$dYNbs_cyl_el[[YNb]] != listTracesTF[el]) {
          values$dYNbs_cyl_el[[YNb]] <- listTracesTF[el]
        }
      }
    })
    }
  })

  output$YNbuttons <- renderUI({
    req(values$NrOfTraces)
    lapply(1:values$NrOfTraces, function(el) {
      YNb <- YNElement(el)
      if(values$Linked_FP1) {
        if(values$dYNbs_cyl_el[[YNb]] == TRUE) {
        div(actionButton(inputId = YNb, label = icon("check"), 
                         style = "color: #339FFF;   background-color: white;  border-color: #339FFF;height: 34px; width: 34px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                         onclick = "toggleLegend(this.id);"))
      } else {
        div(actionButton(inputId = YNb, label = icon("times"), 
                         style = "color: #ff4d4d;   background-color: white;  border-color: #ff4d4d;height: 34px; width: 34px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                         onclick = "toggleLegend(this.id);"))
      }
      } 
    })
  }) 


  flipYNb_FP1 <- function(idx){
    YNb <- YNElement(idx)
    values$dYNbs_cyl_el[[YNb]] <- !values$dYNbs_cyl_el[[YNb]]
  }

  observe({
    lapply(1:values$NrOfTraces, function(ob) {
      YNElement <- YNElement(ob)
      observeEvent(input[[YNElement]], {
        if(!values$Linked_FP1) { flipYNb_FP1(ob) }
        }, ignoreInit = T)
    })
  })

}
shinyApp(ui, server)

解决方案: 经过一番与一些意外行为的斗争后,我发现我需要删除 switchExt-plot1 的观察者以阻止按钮翻转两次。

observeEvent(input[['SwitchExt-plot1']], { 
         flipYNb_FP1(as.numeric(input$SelectTrace)) 
      })

工作应用程序是这样的:

library(plotly)
library(shiny)
library(htmlwidgets)

## js to link buttons to legend
js1 <- c(
  "function toggleLegend(id){",
  "  var ids = id.split('-');",
  "  var plotid = ids[1];",
  "  var index = parseInt(ids[2])-1;", ## correct as the YN buttons are named YesNo-plot1-%d
  "  var plot = document.getElementById(plotid);",
  "  var data = plot.data;",
  "  var v0 = data[index].visible || true;",
  "  var v = v0 == true ? 'legendonly' : true;",
  "  Plotly.restyle(plot, {visible: v}, [index]);",
  "}",
  "function toggleLegend2(id){",
  "  var index = parseInt($('#SelectTrace').val())-1;",
  "  var ids = id.split('-');",
  "  var plotid = ids[1];",
  "  var plot = document.getElementById(plotid);",
  "  var data = plot.data;",
  "  var v0 = data[index].visible || true;",
  "  var v = v0 == true ? 'legendonly' : true;",
  "  Plotly.restyle(plot, {visible: v}, [index]);",
  "}")

## js code to link legend to buttons
js2 <- 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);",
  "  });",
  "}")



YNElement <-    function(idx){sprintf("YesNo-plot1-%d", idx)}


ui <- fluidPage(
  tags$head(
    tags$script(HTML(js1))
  ),
  fluidRow(
    column(2,
           h5("Keep/Drop choices linked to colorscheme 1"),
           uiOutput('YNbuttons')
    ),
    column(8,
           plotlyOutput("plot1")
    ),
    column(2,
           h5('New Button that does not work on legend.', style = 'font-weight:bold'),
           uiOutput('newswitch'),
           br(),
           selectInput(inputId = 'SelectTrace', label = 'Select Trace', choices = 1:3, selected = 1)
           ), style = "margin-top:150px"
  )

)

server <- function(input, output, session) {
  values <- reactiveValues(Linked_FP1 = T, NrOfTraces = length(unique(mtcars$cyl)))

output$plot1 <- renderPlotly({
    if(values$Linked_FP1) {colors <- c('red', 'blue', 'black') } else {colors <- c('black', 'orange', 'gray')}
    p1 <- plot_ly()
    p1 <-  add_trace(p1, data = mtcars, x = ~disp, y = ~mpg, type = 'scatter', mode = 'markers', color = ~as.factor(cyl), colors = colors)
    p1 <- layout(p1, title = 'mtcars group by cyl with switching colors')
    p1 <- plotly_build(p1)

    isolate({ if(values$Linked_FP1) { for(i in seq_along(p1$x$data)){   ## causes the plot to render with previous hide/show selection 
      p1$x$data[[i]]$visible <- values$legenditems[[p1$x$data[[i]]$name]]}  
    } }) ##This block is isolated because otherwise the plot will re-render when the user clicks 1 of the three buttons

    p1 %>% onRender(js2, data = "tracesPlot1")  ## add the javacode to extract the legend status
  })


  observeEvent(values$NrOfTraces, { 
    values$dYNbs_cyl_el <- rep(T,values$NrOfTraces) ## the list of Yes/No status of groups, from which the 3 buttons on the left are build blue or red
    names(values$dYNbs_cyl_el) <- sapply(1:values$NrOfTraces, function(x) {YNElement(x)}) ## add names to that list
    values$legenditems <- rep(T, values$NrOfTraces)  ## make the legenditem list so that the app doesn't crash when user clicks switchExt without first clicking on legend items
    names(values$legenditems) <- sort(unique(mtcars$cyl)) ## add names to that list

  })


  output$newswitch <- renderUI({ 
    req(input$SelectTrace)
    if(values$dYNbs_cyl_el[as.numeric(input$SelectTrace)]) { 
      actionButton(inputId = 'SwitchExt-plot1', label = icon('refresh'), style = "color: #339fff;   background-color: white;  border-color: #339fff;
                   height: 40px; width: 40px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                   onclick = "toggleLegend2(this.id)")
    }
    else { actionButton(inputId = 'SwitchExt-plot1', label = icon('refresh'), style = "color: #f7ad6e;   background-color: white;  border-color: #f7ad6e;
                        height: 40px; width: 40px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                        onclick = "toggleLegend2(this.id)")}
  })


  observeEvent(input$tracesPlot1, {
    if(values$Linked_FP1) {
      listTraces <- input$tracesPlot1
      values$legenditems <- listTraces  ## store the list of show/hide for when the plot re-renders here
      listTracesTF <- gsub('legendonly', FALSE, listTraces)
      listTracesTF <- as.logical(listTracesTF)
      lapply(1:values$NrOfTraces, function(el) {
        if(el <= length(listTracesTF)) {
          YNb <- YNElement(el)
          if(values$dYNbs_cyl_el[[YNb]] != listTracesTF[el]) {
            values$dYNbs_cyl_el[[YNb]] <- listTracesTF[el]
          }
        }
      })
    }
  })

  output$YNbuttons <- renderUI({
    req(values$NrOfTraces)
    lapply(1:values$NrOfTraces, function(el) {
      YNb <- YNElement(el)
      if(values$Linked_FP1) {
        if(values$dYNbs_cyl_el[[YNb]] == TRUE) {
          div(actionButton(inputId = YNb, label = icon("check"), 
                           style = "color: #339FFF;   background-color: white;  border-color: #339FFF;height: 34px; width: 34px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                           onclick = "toggleLegend(this.id);"))
        } else {
          div(actionButton(inputId = YNb, label = icon("times"), 
                           style = "color: #ff4d4d;   background-color: white;  border-color: #ff4d4d;height: 34px; width: 34px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px", 
                           onclick = "toggleLegend(this.id);"))
        }
      } 
    })
  }) 


  flipYNb_FP1 <- function(idx){
    YNb <- YNElement(idx)
    values$dYNbs_cyl_el[[YNb]] <- !values$dYNbs_cyl_el[[YNb]]
  }

  observe({
    lapply(1:values$NrOfTraces, function(ob) {
      YNElement <- YNElement(ob)
      observeEvent(input[[YNElement]], {
        if(!values$Linked_FP1) { flipYNb_FP1(ob) }
      }, ignoreInit = T)
    })
  })

    }
shinyApp(ui, server)

支持评论的图片: enter image description here

最佳答案

我不确定我是否理解,但让我们从一些事情开始。

js1 <- c(
  "function toggleLegend(id){",
  "  var ids = id.split('-');",
  "  var plotid = ids[1];",
  "  var index = parseInt(ids[2])-1;",
  "  var plot = document.getElementById(plotid);",
  "  var data = plot.data;",
  "  var v0 = data[index].visible || true;",
  "  var v = v0 == true ? 'legendonly' : true;",
  "  Plotly.restyle(plot, {visible: v}, [index]);",
  "}",
  "function toggleLegend2(){",
  "  var index = parseInt($('#SelectTrace').val())-1;",
  "  var plot = document.getElementById('plot1');",
  "  var data = plot.data;",
  "  var v0 = data[index].visible || true;",
  "  var v = v0 == true ? 'legendonly' : true;",
  "  Plotly.restyle(plot, {visible: v}, [index]);",
  "}")

actionButton(inputId = 'SwitchExt', ......, onclick = "toggleLegend2()")

这是你想要的吗?

关于javascript - 如何使用通用按钮+ selectInput 通过R Shiny 中的javascript更改轨迹n的图例图例状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55068763/

相关文章:

javascript - 你能用 React Native 跟踪背景地理定位吗?

r - 无法将类型 'closure' 强制转换为类型 'character' 的向量

r - 在没有公共(public)列的情况下水平组合数据框

r - 在 shinyapps.io 上部署打包的 shiny-app

r - session 结束时的计时事件

php - 如何获取按钮以在 jQuery 确认对话框上执行操作?

javascript - 提交一个输入框或一个选择框(不能同时提交)

Javascript null 合并帮助,我如何合并阈值?一 = b || c 但如果 b > d,选择 c

r - 如何在 R 中绘制带有预先计算统计数据的 ggplot2 箱线图?

c - C 中的 Uniroot 函数