Javascript 如何在不重新渲染的情况下更新R Shining中plotly小部件的显示/隐藏跟踪以响应actionbuttons
我正在寻找一种方法,将操作按钮点击链接到Javascript 如何在不重新渲染的情况下更新R Shining中plotly小部件的显示/隐藏跟踪以响应actionbuttons,javascript,r,shiny,plotly,r-plotly,Javascript,R,Shiny,Plotly,R Plotly,我正在寻找一种方法,将操作按钮点击链接到图例状态true/legendoly,它不会导致绘图对象的重新呈现,而是改变小部件。当前底部的演示应用程序确实实现了链接,但它是通过完全重新绘制绘图来实现的 我的目标是将指示保留/删除群集的按钮链接到图形中双向数据的可视化,以更新图形,而不是渲染图形。我当前的解决方案确实会导致完全渲染 交互作用是,即按钮更改图例/绘图&图例更改按钮 我添加了一些图片来解释工作流程 我在更大的实际应用程序中为更大的绘图构建了一个测试版本,其中用户有以下视图: 在这里,用户
图例
状态true/legendoly
,它不会导致绘图
对象的重新呈现,而是改变小部件
。当前底部的演示应用程序确实实现了链接,但它是通过完全重新绘制绘图来实现的
我的目标是将指示保留/删除群集的按钮链接到图形中双向数据的可视化,以更新图形,而不是渲染图形。我当前的解决方案确实会导致完全渲染
交互作用是,即按钮更改图例/绘图&图例更改按钮
我添加了一些图片来解释工作流程
我在更大的实际应用程序中为更大的绘图构建了一个测试版本,其中用户有以下视图:
在这里,用户可以通过in/out按钮选择要删除哪些集群以进行进一步处理
由于前面的问题,我现在有了一个测试应用程序,其中:
-1单击图例会更改绘图和左侧的按钮状态,因此用户可以使用绘图进行输入/输出选择
-2每当绘图重新渲染时,它现在也会重新激活每个轨迹的上一个显示/隐藏状态
第1点是工作流程:
第二点是在onRender
if(values$colors) { for(i in seq_along(p1$x$data)){
p1$x$data[[i]]$visible <- values$legenditems[[p1$x$data[[i]]$name]]}
}
p1 %>% onRender(js, data = "tracesPlot1")
if(值$colors){for(i在seq_中沿(p1$x$data)){
p1$x$data[[i]]$visible%onRender(js,data=“tracesPlot1”)
目前还有第三种交互,当用户单击按钮时,会导致跟踪隐藏。这种方法就是这里的问题。它目前遵循下图中的橙色流程,但我希望通过javascript解决方案来改变这一点,避免重新渲染绘图:
演示应用程序
library(plotly)
library(shiny)
library(htmlwidgets)
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);",
" });",
"}")
YNElement <- function(idx){sprintf("YesNo_button-%d", idx)}
ui <- fluidPage(
fluidRow(
column(2,
h5("Keep/Drop choices linked to colorscheme 1"),
uiOutput('YNbuttons')
),
column(8,
plotlyOutput("plot1")
),
column(2,
h5('Switch grouping'),
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")
), style = "margin-top:150px"
),
verbatimTextOutput("tracesPlot1"),
verbatimTextOutput("tracesPlot2")
)
server <- function(input, output, session) {
values <- reactiveValues(colors = T, NrOfTraces = length(unique(mtcars$cyl)))
output$plot1 <- renderPlotly({
print('plotting!')
if(values$colors) { colors <- c('red', 'blue', 'green') } 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)
if(values$colors) { for(i in seq_along(p1$x$data)){
p1$x$data[[i]]$visible <- values$legenditems[[p1$x$data[[i]]$name]]}
}
p1 %>% onRender(js, data = "tracesPlot1")
})
observeEvent(input$Switch, { values$colors <- !values$colors })
##### THIS BLOCK links buttons -> plot, but causes it to render all over again
### this interaction is what I would like to replace by javascript
observeEvent(values$dYNbs_cyl_el, {
legenditems <- values$dYNbs_cyl_el
legenditems[which(legenditems == FALSE)] <- 'legendonly'
legenditems[which(legenditems == TRUE )] <- 'TRUE'
names(legenditems) <- sort(unique(mtcars$cyl))
values$legenditems <- as.list(legenditems)
})
observeEvent(values$NrOfTraces, {
values$dYNbs_cyl_el <- rep(T,values$NrOfTraces)
names(values$dYNbs_cyl_el) <- sapply(1:values$NrOfTraces, function(x) {YNElement(x)})
})
output$YNbuttons <- renderUI({
req(values$NrOfTraces)
lapply(1:values$NrOfTraces, function(el) {
YNb <- YNElement(el)
if(values$dYNbs_cyl_el[[YNb]] == T ) {
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"))
} 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"))
}
})
})
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]], {
flipYNb_FP1(ob)
}, ignoreInit = T)
})
})
observeEvent(input$tracesPlot1, {
listTraces <- input$tracesPlot1
values$legenditems <- listTraces ## this line would save the legend status even if we remove the observer for the values$dYNbs_cyl_el list
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$tracesPlot1 <- renderPrint({ unlist(input$tracesPlot1) })
output$tracesPlot2 <- renderPrint({ unlist(values$legenditems) })
}
shinyApp(ui, server)
library(plotly)
图书馆(闪亮)
库(htmlwidgets)
js你能试试这个吗:
library(plotly)
library(shiny)
library(htmlwidgets)
js <- c(
"function(el, x){",
" var data = el.data;",
" $('#btn').on('click', function() {",
" var traceName = $('#selector').val();",
" $.each(data, function(index,value){",
" if(value.name == traceName){",
" var v0 = data[index].visible || true;",
" var v = v0 == true ? 'legendonly' : true;",
" Plotly.restyle(el, {visible: v}, [index]);",
" }",
" });",
" });",
"}")
ui <- fluidPage(
plotlyOutput("plot"),
selectInput("selector", "", choices = c("drat", "wt", "qsec")),
actionButton("btn", "Show/hide")
)
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)
})
}
shinyApp(ui, server)
并且不使用onRender
但这仅适用于一个绘图。如果要将按钮链接到多个绘图,假设按钮id具有表单btn-plot2-5
,则执行以下操作:
js <- 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]);",
"}")
js你确定这是可能的吗?据我所知,shiny一直在重新渲染东西(按需).是的,在前面的问题中,我们使用javascript更改绘图的单个数据点,或者重新设置现有对象的轴的样式,还有更多关于通过JavaScripty对绘图进行更改的内容。是的,这就是我所想的。mmm确实有效,但与听带有ID的按钮列表完全不同,也许这很容易呃,如果我给你发邮件让我们讨论一下的话?嘿,Stephane,我正要回答你,有特定于情节的编辑很好,多个按钮的方向是正确的,但我的情况是一个按钮代表一个轨迹(与您的示例中的绘图版本不同。如果您查看我的虚拟应用程序,右侧的3个按钮代表跟踪1、2、3。stripsplit的技巧应该是完美的。我将对其进行一些编辑,我猜,因为我的按钮从1-n运行,我从0-nok知道跟踪,我添加了以下内容:“var trace=index-1,并在测试应用程序中使用javascript中的向下跟踪替换索引,但我发现我的测试应用程序中无法使用它,因为在循环中使用添加标记进行绘图的方法非常不同。有没有一种方法可以简单地链接javascript以按数字而不是按名称切换跟踪?那样的方法无论(在我的真实应用程序中)记录道是1,2,3etc还是'name1',name2,…。按钮总是以nr结尾,记录道总是有一个从0到n的序列nr“按数字切换记录道”:这正是代码所做的。我不理解问题所在。您好@马克。在onRender
的data
参数中,您必须传递绘图的id:data=“plot1”
,而不是data=“tracesPlot1”
。
js <- c(
"function(el, x, plotid){",
" var id = el.getAttribute('id');",
" if(id == plotid){",
" var data = el.data;",
" $('#btn').on('click', function() {",
" var traceName = $('#selector').val();",
" $.each(data, function(index,value){",
" if(value.name == traceName){",
" var v0 = data[index].visible || true;",
" var v = v0 == true ? 'legendonly' : true;",
" Plotly.restyle(el, {visible: v}, [index]);",
" }",
" });",
" });",
" }",
"}")
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, data = "plot")
})
js <- c(
"function(el, x, plotid){",
" var id = el.getAttribute('id');",
" if(id == plotid){",
" var data = el.data;",
" $('[id^=btn]').on('click', function() {",
" var index = parseInt(this.id.split('-')[1]);",
" var v0 = data[index].visible || true;",
" var v = v0 == true ? 'legendonly' : true;",
" Plotly.restyle(el, {visible: v}, [index]);",
" });",
" }",
"}")
ui <- fluidPage(
plotlyOutput("plot"),
actionButton("btn-0", "drat"),
actionButton("btn-1", "wt")
)
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, data = "plot")
})
}
shinyApp(ui, server)
js <- c(
"function toggleLegend(id){",
" var plot = document.getElementById('plot1');",
" var data = plot.data;",
" var index = parseInt(id.split('-')[1]) - 1;",
" var v0 = data[index].visible || true;",
" var v = v0 == true ? 'legendonly' : true;",
" Plotly.restyle(plot, {visible: v}, [index]);",
"}")
ui <- fluidPage(
tags$head(
tags$script(HTML(js))
),
fluidRow(
......
output$YNbuttons <- renderUI({
req(values$NrOfTraces)
lapply(1:values$NrOfTraces, function(el) {
YNb <- YNElement(el)
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);"))
}
})
})
js <- 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]);",
"}")