0

I have included a modal component into my dash application poping up after clicking on a data point inside a graph. I would like however to show the modal only after a second click on the data point confirming that the user really wants the modal to appear and not only clicked by accident. For the moment, I use the ‘Clickdata’ property of the graph component to trigger the callback responsible for the modal. Is there some other property that records how many time a data point was clicked for example, that way I could set the condition to number_of_clicks = 2 (or something alike … ) .

Thank you all for any helpful insight.

Derek O
  • 16,770
  • 4
  • 24
  • 43

1 Answers1

0

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.

enter image description here

Derek O
  • 16,770
  • 4
  • 24
  • 43