Search code examples
pythonplotlyplotly-dashplotly-pythonjupyterdash

Plotly-Dash: How to code interactive callbacks for hover functions in plotly dash


Is it possible to have a text field at the bottom of a graph in dash that displays the text for the point they are on (showing hover data as plain text). So the text box will be able to make changes when users hover over a certain point. I have defined a dcc.Graph component and the app layout but am not sure how to define the callback function for the hoverdata.

I have used the below code to define dcc.Graph and app.layout

fig = go.Figure(data=plot_data, layout=plot_layout)

app.layout = html.Div([
    dcc.Graph(figure=fig),
    
        html.Div([
                    dcc.Markdown(id='mpg-metrics')
                ],style={'width':'20%','display':'inline-block'})
])

Any help with the callback will be great. thanks in advance


Solution

  • Yes, that's very possible! Since you haven't provided a complete description of your setup, I've put together a minimal example that draws on elements from dash.plotly.com/interactive-graphing and https://community.plotly.com/: Use Hover Trace As Input for Callback that among other things describes the use of hover data in callbacks. The code snippet below will produce the following app for JupyterDash. If you'd like to run a standard dash app, just rewrite it following these steps.

    The solution I've put together should do exactly what you're aiming for. Every time you hover over a point on one of the lines in the figure in the dcc.Graph component, a set of details about the trace is displayed in the html.Pre component under it, such as x and y values. Try it out and let me know how it works out for you!

    App 1:

    enter image description here

    If you'd like to retrieve only certain elements of the output, you can subset the output like this:

    json.dumps({'Date:':hoverData['points'][0]['x'],
                'Value:':hoverData['points'][0]['y']}, indent = 2)
    

    App 2:

    enter image description here

    Complete code for JupyterDash, App1

    import json
    from textwrap import dedent as d
    import pandas as pd
    import plotly.graph_objects as go
    import numpy as np
    import dash
    import dash_core_components as dcc
    import dash_html_components as html
    import plotly.express as px
    from dash.dependencies import Input, Output
    from jupyter_dash import JupyterDash
    
    # app info
    app = JupyterDash(__name__)
    
    styles = {
        'pre': {
            'border': 'thin lightgrey solid',
            'overflowX': 'scroll'
        }
    }
    
    # data and basic figure
    x = np.arange(20)+10
    
    fig = go.Figure(data=go.Scatter(x=x, y=x**2, mode = 'lines+markers'))
    fig.add_traces(go.Scatter(x=x, y=x**2.2, mode = 'lines+markers'))
    
    app.layout = html.Div([
        dcc.Graph(
            id='basic-interactions',
            figure=fig,
        ),
    
        html.Div(className='row', children=[
            html.Div([
                dcc.Markdown(d("""
                  Click on points in the graph.
                """)),
                html.Pre(id='hover-data', style=styles['pre']),
            ], className='three columns'),
        ])
    ])
    
    
    @app.callback(
        Output('hover-data', 'children'),
        [Input('basic-interactions', 'hoverData')])
    def display_hover_data(hoverData):
        return json.dumps(hoverData, indent=2)
    
    app.run_server(mode='external', port = 8070, dev_tools_ui=True,
              dev_tools_hot_reload =True, threaded=True)
    

    Complete code for JupyterDash, App2

    import json
    from textwrap import dedent as d
    import pandas as pd
    import plotly.graph_objects as go
    import numpy as np
    import dash
    import dash_core_components as dcc
    import dash_html_components as html
    import plotly.express as px
    from dash.dependencies import Input, Output
    from jupyter_dash import JupyterDash
    
    # app info
    app = JupyterDash(__name__)
    
    styles = {
        'pre': {
            'border': 'thin lightgrey solid',
            'overflowX': 'scroll'
        }
    }
    
    # data and basic figure
    y = np.arange(100)+10
    x = pd.date_range(start='1/1/2021', periods=len(y))
    
    fig = go.Figure(data=go.Scatter(x=x, y=y**2, mode = 'lines+markers'))
    fig.add_traces(go.Scatter(x=x, y=y**2.2, mode = 'lines+markers'))
    
    app.layout = html.Div([
        dcc.Graph(
            id='basic-interactions',
            figure=fig,
        ),
    
        html.Div(className='row', children=[
            html.Div([
                dcc.Markdown(d("""
                  Click on points in the graph.
                """)),
                html.Pre(id='hover-data', style=styles['pre']),
            ], className='three columns'),
        ])
    ])
    
    
    @app.callback(
        Output('hover-data', 'children'),
        [Input('basic-interactions', 'hoverData')])
    def display_hover_data(hoverData):
        try:
            return json.dumps({'Date:':hoverData['points'][0]['x'],
                               'Value:':hoverData['points'][0]['y']}, indent = 2)
        except:
            return None
    
    app.run_server(mode='external', port = 8070, dev_tools_ui=True,
              dev_tools_hot_reload =True, threaded=True)