How to capture click and drag events on graph in dash app?

Hi All,

How to get the coordinate values (x-axes and y-axes) on click and drag events (for the selected area of graph) on graph.

I want to display the axes values after selecting any area on the graph.

Thanks

Hey @Manpreet,

I think, this documentation elaborately answers what you are looking for…
Part 3. Interactive Graphing and Crossfiltering | Dash for Python Documentation | Plotly

Cheers!

Hi Berbere,

Thank you for sharing the link. I had reviewed this link but here they have given the coordinate values from relayoutData callback.

I am specially looking the x and y axes values, which range user has selected. Suppose on x-axes we have months and y-axes we have $ value. And if user selected 3-months graph area then we have to display those 3 months name somewhere on the graph (or popup).

I hope we can do this by adding some JavaScript or with any available functionality.

Thanks

Hi @Manpreet, what are you referring to with this exactly? Do you want to select something with box select?

Maybe this is interesting:

Or this?

Hi @AIMPED ,

Thanks for sharing the code and details.

But I am looking how to get the x-axes values if user select any area on graph.

In this image I want to get the x-axes values FY2024Q3 WW8 and FY2024Q3 WW10. And I want to display the message user selected 2 fiscal month data.

I hope you will get about my requirement.

Thanks

Hi @Manpreet

Have you checked the selectedData property on the documentation that I sent you earlier? If I understood correctly, It does exact same thing that you described to @AIMPED with your provided image.

You seem to use the zoom tool instead of the rectangular selection tool. You can select the tool of interest from the right side of any figure.

Cheers!

1 Like

@Manpreet, what @Berbere describes is correct. To get you started:

from dash import Dash, dcc, html, Input, Output
import plotly.graph_objects as go

fig = go.Figure(
    go.Bar(
        x=list('ABCDE'),
        y=[1, 2, 3, 4, 5],
    )
)

# app initialization
app = Dash(
    __name__
)

# app layout
app.layout = html.Div(
    [
        dcc.Graph(
            id='graph',
            figure=fig,
        ),
        html.Pre(id='out')
    ]
)


@app.callback(
    Output('out', 'children'),
    Input('graph', 'selectedData'),
    prevent_initial_call=True
)
def export(data):
    points = data['points']
    labels = [point['label'] for point in points]

    return f'you selected: {labels}'


if __name__ == '__main__':
    app.run_server(debug=True)

Thanks @AIMPED and @Berbere

This is what I am looking the x-axes selected values from selectedData option. But this is coming if we choose “Box Select” from right side of the graph options and I am looking the x-axes values on “zoom in” option.

Because we are displaying fiscal weeks in x-axes and if the data is coming for 2 years then there are 104 weeks will come in x-axes. And user wants to zoom the data for specific weeks with y-axes $ value.

So I want to display the message like “$ values for selected weeks: W1, W2 and W3”

I hope you got an idea about my requirements.

Thanks

Could you please explain, how your requiremnts differ from my solution posted above?

Hi @AIMPED,

The above solution works if user select the “Box Select” option, I want the same feature if user select the 3 weeks data without choosing any right side options.

Please help me and let me know if we can trigger “selectedData” event on “relayoutData” event? (or can we call the selectedData callback on the relayoutData callback.)

Thanks

Are you referring to the zoom?

yes @AIMPED

Try this:

from dash import Dash, dcc, html, Input, Output, clientside_callback, State
import plotly.graph_objects as go

fig = go.Figure(
    go.Bar(
        x=list('ABCDE'),
        y=[1, 2, 3, 4, 5],
    )
)

# app initialization
app = Dash(
    __name__
)

# app layout
app.layout = html.Div(
    [
        dcc.Graph(
            id='graph',
            figure=fig,
        ),
        dcc.Store(id='axis_range'),
        html.Pre(id='out'),
    ]
)

# callback returns the current axis range for the x-axis
clientside_callback(
    """
    function (data, fig) {
        [min, max] = fig.layout.xaxis.range
        return [min, max]
    }
    """,
    Output('axis_range', 'data'),
    Input('graph', 'relayoutData'),
    State('graph', 'figure'),
    prevent_initial_call=True
)


@app.callback(
    Output('out', 'children'),
    Input('axis_range', 'data'),
    State('graph', 'figure'),
    prevent_initial_call=True
)
def decide(axis_range, figure):
    # min and max values of current view
    mn, mx = axis_range

    # extract x-axis values
    # if more than one trace in figure, you will have to adapt this
    org_x = figure['data'][0]['x']

    # check if axis is categorical (string)
    if isinstance(org_x[0], str):
        x = range(len(org_x))
    else:
        x = fig['data'][0]['x']

    # check for each item if in min max range.
    # if so, append to visible
    # this could lead to problems, if the bars are quite thick and the user does
    # not zoom "enough", e.g over the center x-coordinate of the bar
    visible = []
    for idx, val in enumerate(x):
        if mn < val < mx:
            visible.append(idx)

    # prepare output, recreate original axis values
    out = f'FULLY visible right now: {[org_x[i] for i in visible]}'

    return out


if __name__ == '__main__':
    app.run_server(debug=True, port=8053)
2 Likes

Hi @AIMPED,

I really appreciated all your help and support. Yes its working now and I have tested in my application as well.

Thank you so much.

Thanks
:grinning:

1 Like