Использование функции Shiny actionButton() в функции onRender() htmlWidgets
У меня есть приложение Shiny, в котором пользователь может выбирать черные точки на диаграмме рассеяния Plotly с помощью значка Plotly "box select". Точки, выбранные пользователем, будут выделены красным цветом. У меня есть MWE этого приложения ниже:
library(plotly)
library(htmlwidgets)
library(shiny)
ui <- shinyUI(fluidPage(
plotlyOutput("myPlot")
))
server <- shinyServer(function(input, output) {
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + xlim(10,40) +ylim(0,10)
ggPS <- ggplotly(p)
output$myPlot <- renderPlotly(ggPS %>%
onRender("
function(el, x, data) {
var xArr = [];
var yArr = [];
for (a=0; a<data.wt.length; a++){
xArr.push(data.wt[a])
yArr.push(data.mpg[a])
}
Traces=[]
var tracePoints = {
x: yArr,
y: xArr,
hoverinfo: 'none',
mode: 'markers',
marker: {
color: 'black',
size: 4
}
};
Traces.push(tracePoints);
Plotly.addTraces(el.id, Traces);
el.on('plotly_selected', function(e) {
var numSel = e.points.length
var xSel = [];
var ySel = [];
for (a=0; a<numSel; a++){
xSel.push(e.points[a].x)
ySel.push(e.points[a].y)
}
var trace = {
x: xSel,
y: ySel,
mode: 'markers',
marker: {
color: 'red',
size: 4
},
hoverinfo: 'none'
};
Traces.push(trace);
Plotly.addTraces(el.id, Traces);
})
}
", data = list(dat= mtcars, wt=mtcars$wt, mpg=mtcars$mpg)))})
shinyApp(ui, server)
Я сейчас пытаюсь обновить это блестящее приложение, чтобы выбранные черные точки автоматически не становились красными. Вместо этого, после того, как пользователь выберет черные точки, он может нажать на кнопку действия с меткой "Выделить выбранные точки". Если пользователь нажимает эту кнопку действия, выбранные точки становятся красными. Ниже моя попытка заставить это работать. К сожалению, это приложение не работает, и фактически теряет свою функциональность рисования оригинальных черных точек и, в первую очередь, предоставления значка выбора поля.
library(plotly)
library(Shiny)
library(htmlwidgets)
ui <- shinyUI(fluidPage(
plotlyOutput("myPlot"),
actionButton("highlight", "Highlight selected points")
))
server <- shinyServer(function(input, output) {
highlight <- reactive(input$highlight)
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + xlim(10,40) +ylim(0,10)
ggPS <- ggplotly(p)
output$myPlot <- renderPlotly(ggPS %>%
onRender("
function(el, x, data) {
var xArr = [];
var yArr = [];
for (a=0; a<data.wt.length; a++){
xArr.push(data.wt[a])
yArr.push(data.mpg[a])
}
Traces=[]
var tracePoints = {
x: yArr,
y: xArr,
hoverinfo: 'none',
mode: 'markers',
marker: {
color: 'black',
size: 4
}
};
Traces.push(tracePoints);
Plotly.addTraces(el.id, Traces);
el.on('plotly_selected', function(e) {
observeEvent(data.highlightS, {
var numSel = e.points.length
var xSel = [];
var ySel = [];
for (a=0; a<numSel; a++){
xSel.push(e.points[a].x)
ySel.push(e.points[a].y)
}
var trace = {
x: xSel,
y: ySel,
mode: 'markers',
marker: {
color: 'red',
size: 4
},
hoverinfo: 'none'
};
Traces.push(trace);
Plotly.addTraces(el.id, Traces);
})
})
}
", data = list(dat= mtcars, wt=mtcars$wt, mpg=mtcars$mpg, highlightS=highlight())))})
shinyApp(ui, server)
РЕДАКТИРОВАТЬ:
Я хотел включить картинку, чтобы продемонстрировать, к чему я стремлюсь. В основном, если пользователь выбирает 15 точек, показанных ниже, они остаются черными:
Однако, если пользователь выбирает блестящую кнопку "Выделить выделенные точки", то 15 точек станут красными, как показано ниже:
2 ответа
Это был бы предпочтительный способ сделать это, но другое решение, которое я предоставил, работает лучше. Это из-за сюжетной ошибки в R-привязке, которая разбрасывает значения ключа при выделении, поэтому он работает только для одной итерации. После выбора значения ключа больше не будут работать. Если вы попробуете это, вы поймете, что я имею в виду.
Также я не мог заставить работать размеры, как указано выше, это также может быть еще одна ошибка или просто странная проблема дизайна.
Тем не менее, я предоставляю его в любом случае, потому что он, вероятно, когда-нибудь сработает, и это лучшее решение, требующее гораздо меньшего количества кода - или, возможно, кто-то предложит хороший обходной путь для этого.
library(plotly)
library(htmlwidgets)
library(shiny)
library(shinyBS)
library(ggplot2)
ui <- shinyUI(fluidPage(
plotlyOutput("myPlot"),
bsButton("high","Highlight Selected Points",type="toggle")
))
server <- shinyServer(function(input, output) {
m <- mtcars
m$selected <- 0
m$key <- 1:nrow(m)
rv <- reactiveValues(m=m,high=FALSE)
observeEvent(input$high,{req(input$high);rv$high <- !rv$high } )
ggPS <- reactive(ggplotly(ggplot(m, aes(x = wt, y = mpg))))
sel <- reactive(event_data("plotly_selected"))
mvis <- reactive({
rv$high
sdatf <- sel()
isolate({
rv$m$selected <- 0L
rv$m$selected[ sdatf$key ] <- 1L # now select the ones in sdatf
})
print(sdatf) # debugging
return(rv$m)
})
output$myPlot <- renderPlotly({
mvf <- mvis()
mvf$selfac <- factor(mvf$selected,levels=c(0L, 1L))
print(mvf)
highcol <- ifelse(rv$high,"red","black")
clrs <- c("black",highcol)
ggPS() %>% add_markers(data=mvf,x=~wt,y=~mpg,key=~key,
color=~selfac,colors=clrs) %>%
layout(dragmode = "select")
})
})
shinyApp(ui, server)
Хорошо, это то, что вы хотите, я думаю.
Мне пришлось использовать другой подход, так как я не думаю, что вы можете добавить заговор el.on
событие, как это, но на самом деле сюжет имеет блестящий event_data("plotly_selected")
конструкция предназначена только для такого рода вещей - так что я использовал это вместо.
Основные изменения, которые я сделал:
- сохранил фрейм данных, с которым мы работаем (
m
) вreactiveValues
так что вы можете легко получить к нему доступ (без использования << -) из реактивного узла. - добавил
reactive(event_data(..
чтобы получить блестящие данные выбора. - добавил
selected
колонке к нашему рабочему фрейму данных, чтобы отслеживать то, что должно быть отмечено красным. - добавил
reactive
узел (mvis) для обработки выбранных данных по мере их поступления и пометки новых вещей как выбранных. Также изолировал узел от события responsetiveValueisolate
чтобы избежать бесконечной цепочки реакций. - избавился от
el.on(plotly_selected
потому что я не вижу, как это могло бы работать (хотя, вероятно, есть способ). - включил выбор маркеров с
layout(dragmode="select")
вызов - добавлен второй след для черных маркеров, один для выбранных
- добавили ключевую переменную, чтобы мы могли отслеживать, как нанесенные маркеры соответствуют нашим строкам данных.
- и несколько других мелких мелких изменений (
xArr
а такжеyArr
были перепутаны в оригиналеtraceBlock
определение например) - добавлена кнопка переключения начальной загрузки, чтобы выделить селектор (как позже будет запрошено). Обратите внимание, что для переключения требуется двойной щелчок.
Итак, вот код:
library(plotly)
library(htmlwidgets)
library(shiny)
library(shinyBS)
library(ggplot2)
ui <- shinyUI(fluidPage(
plotlyOutput("myPlot"),
bsButton("high","Highlight Selected Points",type="toggle")
))
server <- shinyServer(function(input, output) {
m <- mtcars
m$selected <- 0
rv <- reactiveValues(m=m,high=FALSE)
observeEvent(input$high,{req(input$high);rv$high <- !rv$high } )
ggPS <- reactive(ggplotly(ggplot(m, aes(x = wt, y = mpg))))
sel <- reactive(event_data("plotly_selected"))
mvis <- reactive({
rv$high
sdatf <- sel()
print(rv$high)
isolate({
rv$m$selected <- 0
rv$m$selected[ sdatf$key ] <- 1 # now select the ones in sdatf
})
# print(sdatf) # debugging
return(rv$m)
})
jscode <-
"function(el, x, data) {
Traces=[]
var xArrNrm = [];
var yArrNrm = [];
var idxNrm = [];
var xArrSel = [];
var yArrSel = [];
var idxSel = [];
for (a=0; a<data.mvis.length; a++){
if(data.mvis[a]['selected']===0){
xArrNrm.push(data.mvis[a]['wt'])
yArrNrm.push(data.mvis[a]['mpg'])
idxNrm.push(a+1)
} else {
xArrSel.push(data.mvis[a]['wt'])
yArrSel.push(data.mvis[a]['mpg'])
idxSel.push(a+1)
}
}
console.log(data.mvis.length)
console.log(data)
var tracePointsNrm = {
x: xArrNrm,
y: yArrNrm,
key: idxNrm,
hoverinfo: 'none',
mode: 'markers',
marker: {
color: 'black',
size: 4
}
};
var tracePointsSel = {
x: xArrSel,
y: yArrSel,
key: idxSel,
hoverinfo: 'none',
mode: 'markers',
marker: {
color: 'red',
size: 6
}
};
if (!data.high){
tracePointsSel.marker.color = 'black'
}
// console.log(tracePointsNrm) // debuging
// console.log(tracePointsSel)
Traces.push(tracePointsNrm);
Traces.push(tracePointsSel);
Plotly.addTraces(el.id, Traces);
}"
output$myPlot <- renderPlotly({
ggPS() %>% onRender(jscode,data=list(mvis=mvis(),high=rv$high)) %>%
layout(dragmode = "select")
})
})
shinyApp(ui, server)
И вот снимок экрана:
Замечания:
Это кажется слишком сложным, и это так. Теоретически это можно сделать без использования onRender
и любой JavaScript, добавив следы с add_marker
и настройка key
приписывать. К сожалению, в привязке R, похоже, есть заговор, который скремблирует значения ключей после выбора, когда вы делаете это таким образом. Слишком плохо - это было бы намного короче и легче для понимания - возможно, это будет в конечном счете исправлено.