I’m working on interactive plots in Jupyter Notebook. I have a scatter plot figure where traces may be added that I want to remove then. I would like to do so by clicking on them.
I am trying to use the
on_click method : when a trace is added to the figure
click_remove callback is consequently added for this trace by using
fig.data tuple is updated, and the “can only assign to data a permutation of a subset of data” condition is verified.
However, it ends up with
KeyError. It seems that it is because all traces, even those not clicked, are “refreshed” (or something like that), and at some point the indexation is broken because a trace was removed.
I have found a workaround by setting
visible=False for wanted traces, but it’s not really satisfying. I also tried using
fig.data[-1]._select_callbacks.append(click_remove) but the callback seems to never be called then.
Below is a simple Jupyter Notebook code to replicate the behavior (first post here, let me know if there is a better way to provide code). For this exemple I got following errors:
IndexError: tuple index out of range, if clicking on trace 0 to 3.
KeyError: 4, if clicking on trace 4 (the last added).
import numpy as np import plotly.graph_objects as go from IPython.display import display def click_remove(trace, points, state): print(points.trace_name) # skip if clicking on another trace # because when clicking on a trace # all traces are activating this callback if not points.point_inds: print(' Skip.') return print(' Remove.') idx = points.trace_index new_data = list(fig.data) new_data.pop(idx) # can only assign to data a permutation of a subset of data assert np.all( np.array([id(t) for i, t in enumerate(fig.data) if not i == idx ]) == np.array([id(t) for t in new_data]) ) fig.data = new_data # Workaround by setting `visible=False` # fig_spectrum.data[points.trace_index].visible = False nlines, npoints = 5, 100 lines = [np.random.randn(npoints)+5*i for i in range(nlines)] fig = go.FigureWidget(layout=dict(hovermode='closest')) for line in lines: fig.add_trace(go.Scatter(y=line)) # What I would like to do fig.data[-1].on_click(click_remove) # I also tried using clickmode='select' and following line: #fig.data[-1]._select_callbacks.append(click_remove) # Even setting the callback on only one trace will throw same errors #fig.data.on_click(click_remove) display(fig)