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

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)
3 Likes