I’m working on a functionality which adds traces to a plot based on user preferences , since traces need to be removed as well, i need to come up a easy way to remove traces from plot. My code (given below) implements this by using dash-extensions EventListerner and restyleData attribute of dcc.graph.
When control key is pressed & a trace in legend is clicked, it removes a trace. The problem that im currently facing is that, it doesnt work reliably - sometimes it removes the trace, some times it doesn’t - just disables it.
import dash
from dash_extensions.enrich import dcc
from dash_extensions.enrich import html
from dash.dependencies import Input, Output
from dash_extensions.enrich import DashProxy, html, Input, Output, State
from dash_extensions import EventListener
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go
event = {"event": "keydown", "props": ['ctrlKey']}
kwargs = {
"meta_tags": [{"name":"viewport", "content":"width=device-width, initial-scale=1, shrink-to-fit=no", "charset": "utf-8"}],
"title" : "Event Listener",
}
app = DashProxy(__name__, **kwargs)
def dummy_graph():
fig = go.Figure()
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[1,2,3], name=f'Trace 1'))
fig.add_trace(go.Scatter(x=[4, 5, 6], y=[1,2,3], name=f'Trace 2'))
fig.add_trace(go.Scatter(x=[7, 8, 9], y=[1,2,3], name=f'Trace 3'))
return fig
app.layout = html.Div(
[
EventListener(
events=[event], logging=True, id="el",
),
dcc.Graph(
id="graph",
figure=dummy_graph()
),
html.Div(id="output"),
html.Div(id="log")
]
)
@app.callback(
[Output("output","children"),
Output("el","event"),
Output("graph","figure")],
[Input("graph", "restyleData"),
Input("el", "event"),
Input("graph", "figure")],
prevent_initial_call=True
)
def display_relayout_data(relayout_data, e,figure):
triggered_input = dash.callback_context.triggered[0]["prop_id"].split(".")[0]
if triggered_input == "graph":
# return str(triggered_input)
if e is not None:
if e['ctrlKey'] == True:
figure['data'].pop(relayout_data[1][0])
return "", None, figure
else:
return "", None, figure
return "", None, figure
if __name__ == "__main__":
app.run_server(debug=True)
You may notice that im returning event attribute as None in callback, that is because i noticed that it needs to be cleared every time im done with it otherwise it retains its state and a trace is deleted even if cntrl key is not pressed.
I need to some input on this implementation - is there a cleaner and more reliable way to achieve what im doing ?
As show below in video below - im holding down cntrl key while also clicking traces - it doesn’t work smoothly and seems buggy.