- I can click a button to clear all current selections.
You could modify the callback to-assign the relevant selection data to None
when a button is clicked. I modified the example to illustrate this behaviour. I used a global variable to make sure 1 click = 1 reset and we use the same callback; there’s probably a better way.
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import pandas as pd
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# make a sample data frame with 6 columns
np.random.seed(0)
df = pd.DataFrame({"Col " + str(i+1): np.random.rand(30) for i in range(6)})
total_clicks = 0 # keep track of total clicks
app.layout = html.Div([
html.Button('Reset selections', id='reset'), # add a button
html.Div(
dcc.Graph(id='g1', config={'displayModeBar': False}),
className='four columns'
),
html.Div(
dcc.Graph(id='g2', config={'displayModeBar': False}),
className='four columns'
),
html.Div(
dcc.Graph(id='g3', config={'displayModeBar': False}),
className='four columns'
)
], className='row')
def get_figure(df, x_col, y_col, selectedpoints, selectedpoints_local):
if selectedpoints_local and selectedpoints_local['range']:
ranges = selectedpoints_local['range']
selection_bounds = {'x0': ranges['x'][0], 'x1': ranges['x'][1],
'y0': ranges['y'][0], 'y1': ranges['y'][1]}
else:
selection_bounds = {'x0': np.min(df[x_col]), 'x1': np.max(df[x_col]),
'y0': np.min(df[y_col]), 'y1': np.max(df[y_col])}
# set which points are selected with the `selectedpoints` property
# and style those points with the `selected` and `unselected`
# attribute. see
# https://medium.com/@plotlygraphs/notes-from-the-latest-plotly-js-release-b035a5b43e21
# for an explanation
return {
'data': [{
'x': df[x_col],
'y': df[y_col],
'text': df.index,
'textposition': 'top',
'selectedpoints': selectedpoints,
'customdata': df.index,
'type': 'scatter',
'mode': 'markers+text',
'marker': { 'color': 'rgba(0, 116, 217, 0.7)', 'size': 12 },
'unselected': {
'marker': { 'opacity': 0.3 },
# make text transparent when not selected
'textfont': { 'color': 'rgba(0, 0, 0, 0)' }
}
}],
'layout': {
'margin': {'l': 20, 'r': 0, 'b': 15, 't': 5},
'dragmode': 'select',
'hovermode': False,
# Display a rectangle to highlight the previously selected region
'shapes': [dict({
'type': 'rect',
'line': { 'width': 1, 'dash': 'dot', 'color': 'darkgrey' }
}, **selection_bounds
)]
}
}
# this callback defines 3 figures
# as a function of the intersection of their 3 selections
@app.callback(
[Output('g1', 'figure'),
Output('g2', 'figure'),
Output('g3', 'figure')],
[Input('g1', 'selectedData'),
Input('g2', 'selectedData'),
Input('g3', 'selectedData'),
Input('reset', 'n_clicks')] # get the button click data
)
def callback(selection1, selection2, selection3, n_clicks): # add the click data to the callback
# reset selectionN and selectedpoints
global total_clicks
if n_clicks and n_clicks > total_clicks:
selectedpoints = selection1 = selection2 = selection3 = None
total_clicks = n_clicks
else:
selectedpoints = df.index
for selected_data in [selection1, selection2, selection3]:
if selected_data and selected_data['points']:
selectedpoints = np.intersect1d(selectedpoints,
[p['customdata'] for p in selected_data['points']])
return [get_figure(df, "Col 1", "Col 2", selectedpoints, selection1),
get_figure(df, "Col 3", "Col 4", selectedpoints, selection2),
get_figure(df, "Col 5", "Col 6", selectedpoints, selection3)]
if __name__ == '__main__':
app.run_server(debug=True)