Im trying to synchronize selections between a table and a graph in both directions. So when a point is selected on the graph i’d like to select the corresponding rows in a table and when rows are selected in the table the corresponding points in the graph should be selected.
here is a minimal prototype, it uses selected_rows
to update the table and selectedpoints
in the graph to update the graph.
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table as dt
from dash.dependencies import Input, Output
app = dash.Dash("simple repro")
figure_data = {
"data": [{
"x": [1, 2, 3],
"y": [1, 2, 3],
'selectedpoints': [],
"mode": "markers",
'marker': {'size': 20}
}],
'layout': {
'clickmode': 'event+select'
}
}
app.layout = html.Div(
className="grid",
children=[
html.Div(
[
dcc.Graph(id="graph", figure=figure_data),
dt.DataTable(
id="table",
sorting=True,
row_selectable='multi',
columns=[{"name": "x", "id": "x"}, {"name": "y", "id": "y"}],
data=[
{"x": 1, "y": 1},
{"x": 2, "y": 2},
{"x": 3, "y": 3},
],
)
],
),
],
)
@app.callback(Output("table", "selected_rows"), [Input("graph", "selectedData")])
def update_table_selection(selected_data):
if not selected_data or not selected_data.get('points'):
return []
return [point['pointIndex'] for point in selected_data['points']]
@app.callback(Output("graph", "figure"), [Input("table", "selected_rows")])
def update_graph_selection(selected_rows):
if selected_rows:
figure_data['data'][0]['selectedpoints'] = selected_rows
else:
figure_data['data'][0]['selectedpoints'] = []
return figure_data
if __name__ == "__main__":
app.run_server(debug=True, host="0.0.0.0", port=8050)
This almost works but theres a problem. If you select a row in the table and then shift + click a point on the graph the original row and point selected is lost. I believe this is because when a table row is selected the figure’s selectedpoints
is updated but the selectedData
isn’t so when you click on the graph the information is lost.
But i dont know how to keep those in sync without resulting in an infinite callback loop.