I don't believe there are any properties of a graph component that allow you to store clicks, but you can extract click information through clickData
and store that information in dcc.Store
.
Since you didn't provide any specific code in your question, these are some of the assumptions built into my working example:
- that we are using a DataFrame for the data (which allows us to more conveniently store the number of clicks using a dictionary with the data point's
index
in the dataframe as the key and the number of clicks as the value). for example, dcc.Store
might contain {'0': 1}
meaning that the data point at index 0 has been clicked 1 time.
- that you want the modal component to appear permanently once any point is clicked twice.
- that the modal component you are using has the
style
property, which we will set to {'display': 'none'} so it is not visible when the app first renders, but will become visible once any point is clicked twice (based on this answer). i picked dcc.TextArea
but it could be any modal component that you like (this also works for double clicking on an individual point, thanks to @Hamzah's helpful answer)
import pandas as pd
import plotly.express as px
import dash
from dash import Input, Output, dcc, html, ctx
from typing import List
app = dash.Dash(__name__)
df = pd.DataFrame({
'x': list(range(5,10)),
'y': list(range(5,10))
})
fig = px.scatter(df, x='x', y='y')
fig.update_traces(marker_size=20)
app.layout = html.Div([
html.Div([
dcc.Graph(figure=fig, id='graph-interaction'),
dcc.Store(id='store-clicks')
]),
html.Div([
# Create element to hide/show, in this case an 'Input Component'
dcc.Textarea(
id='modal',
value='This shows up when you click a point twice',
style={'width': 100, 'height': 100},
),
], style= {'display': 'none', 'padding-left': '80px'} # <-- This is the line that will be changed by the dropdown callback
)
])
@app.callback(
Output('modal','style'),
Output('store-clicks','data'),
Output('graph-interaction', 'clickData'),
[Input('graph-interaction', 'clickData'),
Input('store-clicks','data'),
])
def show_selection(click_data, stored_clicks):
# print(f"incoming click data: {click_data}")
if stored_clicks is None:
stored_clicks = {}
else:
point_index = str(click_data['points'][0]['pointIndex'])
if point_index in stored_clicks:
stored_clicks[point_index] += 1
else:
stored_clicks[point_index] = 1
## this means that the model componant will appear permanently
## after any point is clicked twice
if 2 in stored_clicks.values():
return {'display': 'block'}, stored_clicks, None
else:
return {'display': 'none'}, stored_clicks, None
if __name__ == "__main__":
app.run_server(debug=True)
In the example below, I click on the points (6,6)
and (7,7)
one time each, and once I click on (8,8)
twice, the textbox appears.
