From e355d29928c2cb9b27aa62d20b4a63f969d692d3 Mon Sep 17 00:00:00 2001 From: denphi Date: Wed, 1 May 2019 12:32:00 -0400 Subject: [PATCH 1/5] added support to plotly_unselect event --- js/src/Figure.js | 15 +++++++++ plotly/basedatatypes.py | 71 +++++++++++++++++++++++++++++++++++++++++ plotly/basewidget.py | 2 ++ 3 files changed, 88 insertions(+) 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..2e39379a35d 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 @@ -4160,6 +4163,60 @@ def on_selection( if callback: self._select_callbacks.append(callback) + # 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_selection 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 selection_fn(trace, points, selector): + ... inds = points.point_inds + ... # Do something + + >>> trace.on_selection(selection_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_selection(self, points, selector): @@ -4176,7 +4233,21 @@ def _dispatch_on_selection(self, for callback in self._select_callbacks: callback(self, points, selector) + 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 = points.point_inds + for callback in self._select_callbacks: + callback(self, points) + class BaseFrameHierarchyType(BasePlotlyType): """ Base class for all types in the trace hierarchy diff --git a/plotly/basewidget.py b/plotly/basewidget.py index a99bd7636c9..294fadd11f1 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_selection(points) self._js2py_pointsCallback = None From e1202804ec44c7fe2809b434e3f138dc3c51f590 Mon Sep 17 00:00:00 2001 From: denphi Date: Wed, 1 May 2019 12:38:33 -0400 Subject: [PATCH 2/5] fixed wrong calls --- plotly/basedatatypes.py | 37 +++++++++++++++++++------------------ plotly/basewidget.py | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/plotly/basedatatypes.py b/plotly/basedatatypes.py index 2e39379a35d..9e025e16d8c 100644 --- a/plotly/basedatatypes.py +++ b/plotly/basedatatypes.py @@ -4162,6 +4162,23 @@ def on_selection( if callback: self._select_callbacks.append(callback) + + def _dispatch_on_selection(self, + points, + selector): + """ + Dispatch points and selector info to selection 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 = points.point_inds + + for callback in self._select_callbacks: + callback(self, points, selector) + # deselect # ------ @@ -4216,23 +4233,7 @@ def on_deselect( if callback: self._deselect_callbacks.append(callback) - - def _dispatch_on_selection(self, - points, - selector): - """ - Dispatch points and selector info to selection 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 = points.point_inds - - for callback in self._select_callbacks: - callback(self, points, selector) - + def _dispatch_on_deselect(self, points): """ @@ -4245,7 +4246,7 @@ def _dispatch_on_deselect(self, # even though these events update the selectedpoints property. self.selectedpoints = points.point_inds - for callback in self._select_callbacks: + for callback in self._deselect_callbacks: callback(self, points) class BaseFrameHierarchyType(BasePlotlyType): diff --git a/plotly/basewidget.py b/plotly/basewidget.py index 294fadd11f1..5359a217206 100644 --- a/plotly/basewidget.py +++ b/plotly/basewidget.py @@ -736,7 +736,7 @@ def _handler_js2py_pointsCallback(self, change): elif event_type == 'plotly_selected': trace._dispatch_on_selection(points, selector) elif event_type == 'plotly_deselect': - trace._dispatch_on_selection(points) + trace._dispatch_on_deselect(points) self._js2py_pointsCallback = None From e87d27f5e7f29d0cc3749f6ee9949bc4cd0a8075 Mon Sep 17 00:00:00 2001 From: denphi Date: Wed, 1 May 2019 12:46:06 -0400 Subject: [PATCH 3/5] Changes in documentation --- plotly/basedatatypes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plotly/basedatatypes.py b/plotly/basedatatypes.py index 9e025e16d8c..fe0261ccccc 100644 --- a/plotly/basedatatypes.py +++ b/plotly/basedatatypes.py @@ -4205,7 +4205,7 @@ def on_deselect( append : bool If False (the default), this callback replaces any previously - defined on_selection callbacks for this trace. If True, + defined on_deselect callbacks for this trace. If True, this callback is appended to the list of any previously defined callbacks. @@ -4218,11 +4218,11 @@ def on_deselect( >>> from plotly.callbacks import Points >>> points = Points() - >>> def selection_fn(trace, points, selector): + >>> def deselect_fn(trace, points): ... inds = points.point_inds ... # Do something - >>> trace.on_selection(selection_fn) + >>> 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 From a67fbd19cab5239dcb7ded5e4685c130fb567d1a Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 3 May 2019 18:41:13 -0400 Subject: [PATCH 4/5] Whitespace --- plotly/basedatatypes.py | 15 +++++++-------- plotlywidget/static/index.js | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/plotly/basedatatypes.py b/plotly/basedatatypes.py index fe0261ccccc..7c0e543ca19 100644 --- a/plotly/basedatatypes.py +++ b/plotly/basedatatypes.py @@ -4162,7 +4162,7 @@ def on_selection( if callback: self._select_callbacks.append(callback) - + def _dispatch_on_selection(self, points, selector): @@ -4179,15 +4179,14 @@ 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 + 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 @@ -4233,9 +4232,8 @@ def on_deselect( if callback: self._deselect_callbacks.append(callback) - - def _dispatch_on_deselect(self, - points): + + def _dispatch_on_deselect(self, points): """ Dispatch points info to deselection callbacks """ @@ -4248,7 +4246,8 @@ def _dispatch_on_deselect(self, for callback in self._deselect_callbacks: callback(self, points) - + + class BaseFrameHierarchyType(BasePlotlyType): """ Base class for all types in the trace hierarchy 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 From 4102f05d7d325703271d455eac7847e58d65b1a2 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 3 May 2019 18:48:33 -0400 Subject: [PATCH 5/5] Set trace selectedpoints property to None instead of () To Plotly.js None represents No selection active, while () represents and active selection with not points selected. --- plotly/basedatatypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/basedatatypes.py b/plotly/basedatatypes.py index 7c0e543ca19..f7a891c973a 100644 --- a/plotly/basedatatypes.py +++ b/plotly/basedatatypes.py @@ -4242,7 +4242,7 @@ def _dispatch_on_deselect(self, points): # 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 = points.point_inds + self.selectedpoints = None for callback in self._deselect_callbacks: callback(self, points)