操作按钮
legend
地位
true/legendonly
rendering
的
plotly
而是改变
widget
我的目标是将表示keep/drop集群的按钮链接到两个方向的绘图中的数据的可视化
. 我当前的解决方案确实会导致完全渲染。
交互作用是,即按钮更改图例/绘图&图例更改按钮。
我添加了一些图片来解释工作流。
我已经在更大的实际应用程序中为更大的情节构建了一个测试版本,其中用户有以下视图:
在这里,用户可以通过in/out按钮选择要移除哪些集群以进行进一步处理。
感谢上一个问题
here
-1单击图例可更改绘图和左侧的按钮状态,因此用户可以使用绘图进行输入/输出选择
-2每当绘图重新呈现时,它现在也会重新激活每个记录道以前的显示/隐藏状态。
第二点是在
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")
目前还有第三个交互,当用户单击按钮时,它会导致跟踪隐藏起来。这种做法是这里的问题。它目前遵循下图中的橙色流程,但我希望通过一个避免重新呈现绘图的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)
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);",
# " });",
# "}")
js2 <- c(
"function(el, x, inputName){",
" var id = el.getAttribute('id');",
" if(id == inputName){",
" var data = el.data;",
" $('[id^=btn]').on('click', function() {",
" var index = parseInt(this.id.split('-')[1]);",
" var trace = index -1; ",
" var v0 = data[trace].visible || true;",
" var v = v0 == true ? 'legendonly' : true;",
" Plotly.restyle(el, {visible: v}, [trace]);",
" });",
" }",
"}")
YNElement <- function(idx){sprintf("btn-%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!')
values$legenditemNames <- sort(unique(mtcars$cyl))
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(js2, data = "tracesPlot1")
})
observeEvent(input$Switch, { values$colors <- !values$colors })
##### THIS BLOCK links buttons -> plot, but causes it to render all over again
# observeEvent(values$dYNbs_cyl_el, {
# legenditems <- values$dYNbs_cyl_el
# legenditems[which(legenditems == FALSE)] <- 'legendonly'
# legenditems[which(legenditems == TRUE )] <- 'TRUE'
# names(legenditems) <- values$legenditemNames
# 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
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)