diff --git a/js/src/Figure.js b/js/src/Figure.js index 754f3eaea34..7c7821f571f 100644 --- a/js/src/Figure.js +++ b/js/src/Figure.js @@ -750,6 +750,10 @@ var FigureView = widgets.DOMWidgetView.extend({ function (update) { that.handle_plotly_selected(update) }); + that.el.on("plotly_deselect", + function (update) { + that.handle_plotly_deselect(update) + }); that.el.on("plotly_doubleclick", function (update) { that.handle_plotly_doubleclick(update) @@ -1080,6 +1084,17 @@ var FigureView = widgets.DOMWidgetView.extend({ handle_plotly_selected: function (data) { this._send_points_callback_message(data, "plotly_selected"); }, + + /** + * Handle plotly_deselect events emitted by the Plotly.js library + * @param data + */ + handle_plotly_deselect: function (data) { + data = { + points : [] + } + this._send_points_callback_message(data, "plotly_deselect"); + }, /** * Build and send a points callback message to the Python side diff --git a/plotly/basedatatypes.py b/plotly/basedatatypes.py index 5f24d95e4a6..f7a891c973a 100644 --- a/plotly/basedatatypes.py +++ b/plotly/basedatatypes.py @@ -3909,6 +3909,9 @@ def __init__(self, plotly_name, **kwargs): # ### Callbacks to be called on selection ### self._select_callbacks = [] + # ### Callbacks to be called on deselect ### + self._deselect_callbacks = [] + # ### Trace index in figure ### self._trace_ind = None @@ -4176,6 +4179,74 @@ def _dispatch_on_selection(self, for callback in self._select_callbacks: callback(self, points, selector) + # deselect + # -------- + def on_deselect( + self, + callback, + append=False): + """ + Register function to be called when the user deselects points + in this trace using doubleclick. + + Note: Callbacks will only be triggered when the trace belongs to a + instance of plotly.graph_objs.FigureWidget and it is displayed in an + ipywidget context. Callbacks will not be triggered on figures + that are displayed using plot/iplot. + + Parameters + ---------- + callback + Callable function that accepts 3 arguments + + - this trace + - plotly.callbacks.Points object + + append : bool + If False (the default), this callback replaces any previously + defined on_deselect callbacks for this trace. If True, + this callback is appended to the list of any previously defined + callbacks. + + Returns + ------- + None + + Examples + -------- + >>> from plotly.callbacks import Points + >>> points = Points() + + >>> def deselect_fn(trace, points): + ... inds = points.point_inds + ... # Do something + + >>> trace.on_deselect(deselect_fn) + + Note: The creation of the `points` object is optional, + it's simply a convenience to help the text editor perform completion + on the `points` arguments inside `selection_fn` + """ + if not append: + del self._deselect_callbacks[:] + + if callback: + self._deselect_callbacks.append(callback) + + def _dispatch_on_deselect(self, points): + """ + Dispatch points info to deselection callbacks + """ + if 'selectedpoints' in self: + # Update the selectedpoints property, which will notify all views + # of the selection change. This is a special case because no + # restyle event is emitted by plotly.js on selection events + # even though these events update the selectedpoints property. + self.selectedpoints = None + + for callback in self._deselect_callbacks: + callback(self, points) + class BaseFrameHierarchyType(BasePlotlyType): """ diff --git a/plotly/basewidget.py b/plotly/basewidget.py index a99bd7636c9..5359a217206 100644 --- a/plotly/basewidget.py +++ b/plotly/basewidget.py @@ -735,6 +735,8 @@ def _handler_js2py_pointsCallback(self, change): trace._dispatch_on_unhover(points, state) elif event_type == 'plotly_selected': trace._dispatch_on_selection(points, selector) + elif event_type == 'plotly_deselect': + trace._dispatch_on_deselect(points) self._js2py_pointsCallback = None diff --git a/plotlywidget/static/index.js b/plotlywidget/static/index.js index 6465ac42026..30374be146d 100644 --- a/plotlywidget/static/index.js +++ b/plotlywidget/static/index.js @@ -13409,6 +13409,10 @@ var FigureView = widgets.DOMWidgetView.extend({ function (update) { that.handle_plotly_selected(update) }); + that.el.on("plotly_deselect", + function (update) { + that.handle_plotly_deselect(update) + }); that.el.on("plotly_doubleclick", function (update) { that.handle_plotly_doubleclick(update) @@ -13739,6 +13743,17 @@ var FigureView = widgets.DOMWidgetView.extend({ handle_plotly_selected: function (data) { this._send_points_callback_message(data, "plotly_selected"); }, + + /** + * Handle plotly_deselect events emitted by the Plotly.js library + * @param data + */ + handle_plotly_deselect: function (data) { + data = { + points : [] + } + this._send_points_callback_message(data, "plotly_deselect"); + }, /** * Build and send a points callback message to the Python side