Skip to content

Commit 74ec12a

Browse files
committed
modularize event logic, add 3d events example
1 parent d8d71ae commit 74ec12a

File tree

4 files changed

+82
-68
lines changed

4 files changed

+82
-68
lines changed

R/shiny.R

+10-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,16 @@ event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selecte
5353
"from within a reactive shiny context.")
5454
}
5555
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
56-
if (event[1] == "plotly_selected" && !is.null(val)) {
57-
data.frame(lapply(val, as.numeric))
56+
57+
# TODO: report this!
58+
if (event[1] == "plotly_selected" && is.character(val)) {
59+
nms <- unique(names(val))
60+
d <- list()
61+
for (i in nms) {
62+
d[[i]] <- val[names(val) %in% i]
63+
}
64+
# TODO: try to convert numerics?
65+
data.frame(d, stringsAsFactors = FALSE)
5866
} else {
5967
val
6068
}

inst/examples/plotly3DEvents/app.R

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library(shiny)
2+
library(plotly)
3+
4+
ui <- fluidPage(
5+
plotlyOutput("plot"),
6+
verbatimTextOutput("hover"),
7+
verbatimTextOutput("click")
8+
)
9+
10+
server <- function(input, output, session) {
11+
12+
output$plot <- renderPlotly({
13+
plot_ly(x = rnorm(10), y = rnorm(10), z = rnorm(10), type = "scatter3d")
14+
})
15+
16+
output$hover <- renderPrint({
17+
d <- event_data("plotly_hover")
18+
if (is.null(d)) "Hover events appear here (unhover to clear)" else d
19+
})
20+
21+
output$click <- renderPrint({
22+
d <- event_data("plotly_click")
23+
if (is.null(d)) "Click events appear here (double-click to clear)" else d
24+
})
25+
26+
}
27+
28+
shinyApp(ui, server, options = list(display.mode = "showcase"))

inst/examples/plotlyEvents/app.R

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ ui <- fluidPage(
1212
server <- function(input, output, session) {
1313

1414
output$plot <- renderPlotly({
15+
# use the key aesthetic/argument to help uniquely identify selected observations
16+
key <- row.names(mtcars)
1517
if (identical(input$plotType, "ggplotly")) {
16-
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
18+
p <- ggplot(mtcars, aes(x = mpg, y = wt, colour = factor(vs), key = key)) +
19+
geom_point()
1720
ggplotly(p) %>% layout(dragmode = "select")
1821
} else {
19-
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
22+
plot_ly(mtcars, x = mpg, y = wt, key = key, mode = "markers") %>%
2023
layout(dragmode = "select")
2124
}
2225
})

inst/htmlwidgets/plotly.js

+39-64
Original file line numberDiff line numberDiff line change
@@ -31,82 +31,57 @@ HTMLWidgets.widget({
3131
Plotly.newPlot(graphDiv, x.data, x.layout);
3232
}
3333

34-
// send user input event data to shiny
35-
if (shinyMode) {
36-
graphDiv.on('plotly_click', function(eventData) {
37-
// extract only the data we may want to access in R
34+
sendEventData = function(eventType) {
35+
return function(eventData) {
36+
if (eventData === undefined || !eventData.hasOwnProperty("points")) {
37+
return null;
38+
}
3839
var d = eventData.points.map(function(pt) {
3940
var obj = {
40-
curveNumber: pt.curveNumber,
41-
pointNumber: pt.pointNumber,
42-
x: pt.x,
43-
y: pt.y
41+
curveNumber: pt.curveNumber,
42+
pointNumber: pt.pointNumber,
43+
x: pt.x,
44+
y: pt.y
4445
};
45-
if (pt.data.hasOwnProperty("key")) {
46-
if (typeof pt.pointNumber === "number") {
47-
obj.key = pt.data.key[pt.pointNumber];
48-
} else {
49-
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
50-
} // TODO: can pointNumber be 3D?
51-
}
52-
return obj;
53-
});
54-
Shiny.onInputChange(".clientValue-plotly_click-" + x.source, d);
55-
});
56-
57-
// clear click selection
58-
graphDiv.on('plotly_doubleclick', function(eventData) {
59-
Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
60-
});
61-
62-
graphDiv.on('plotly_hover', function(eventData) {
63-
// extract only the data we may want to access in R
64-
var d = eventData.points.map(function(pt) {
65-
var obj = {
66-
curveNumber: pt.curveNumber,
67-
pointNumber: pt.pointNumber,
68-
x: pt.x,
69-
y: pt.y
46+
// grab the trace corresponding to this point
47+
var tr = x.data[pt.curveNumber];
48+
// add on additional trace info, if it exists
49+
attachKey = function(keyName) {
50+
if (tr.hasOwnProperty(keyName)) {
51+
if (typeof pt.pointNumber === "number") {
52+
obj[keyName] = tr[keyName][pt.pointNumber];
53+
} else {
54+
obj[keyName] = tr[keyName][pt.pointNumber[0]][pt.pointNumber[1]];
55+
}// TODO: can pointNumber be 3D?
56+
}
7057
};
71-
if (pt.data.hasOwnProperty("key")) {
72-
if (typeof pt.pointNumber === "number") {
73-
obj.key = pt.data.key[pt.pointNumber];
74-
} else {
75-
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
76-
} // TODO: can pointNumber be 3D?
77-
}
78-
return obj;
58+
attachKey("z");
59+
attachKey("key");
60+
return obj;
7961
});
80-
Shiny.onInputChange(".clientValue-plotly_hover-" + x.source, d);
81-
});
82-
83-
// clear hover selection
62+
console.log(d);
63+
Shiny.onInputChange(".clientValue-" + eventType + "-" + x.source, d);
64+
};
65+
};
66+
67+
// send user input event data to shiny
68+
if (shinyMode) {
69+
graphDiv.on('plotly_hover', sendEventData('plotly_hover'));
70+
graphDiv.on('plotly_click', sendEventData('plotly_click'));
71+
graphDiv.on('plotly_selected', sendEventData('plotly_selected'));
8472
graphDiv.on('plotly_unhover', function(eventData) {
8573
Shiny.onInputChange(".clientValue-plotly_hover-" + x.source, null);
8674
});
87-
88-
graphDiv.on('plotly_selected', function(eventData) {
89-
if (eventData !== undefined) {
90-
// convert the array of objects to object of arrays so this converts
91-
// to data frame in R as opposed to a vector
92-
var pts = eventData.points;
93-
var obj = {
94-
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
95-
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
96-
x: pts.map(function(pt) {return pt.x; }),
97-
y: pts.map(function(pt) {return pt.y; })
98-
};
99-
Shiny.onInputChange(".clientValue-plotly_selected-" + x.source, obj);
100-
}
75+
graphDiv.on('plotly_doubleclick', function(eventData) {
76+
Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
10177
});
102-
103-
// clear select/lasso selection & click
78+
// 'plotly_deselect' is code for doubleclick when in select mode
10479
graphDiv.on('plotly_deselect', function(eventData) {
10580
Shiny.onInputChange(".clientValue-plotly_selected-" + x.source, null);
10681
Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
10782
});
108-
109-
} // shinyMode
110-
} // renderValue
83+
}
84+
85+
}
11186

11287
});

0 commit comments

Comments
 (0)