Search code examples
pythonbar-chartplotly-dashplotly-python

Plotly Dash: How to align the tooltip to the top instead of top right


I would like the tooltip (highlighted part of the image below) to appear on the top of the vertical bar, while it is currently on the right.

enter image description here

Even explored documentation of plotly bar charts and saw some tutorials, but I am not able to figure out the solution for this problem.

Data frames and bar chart code:

df = pd.DataFrame({
    "Day": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"],
    "Amount": [4, 1, 2, 2, 4, 5,8],
})

fig = px.bar(df, x="Day", y="Amount",orientation="v",opacity=0.9,barmode='relative',color_discrete_sequence=["purple"],text='Day',template='none')
fig.update_traces(texttemplate = '%{text:.2s}',textposition = 'outside',width = [.3,.3,.3,.3,.3,.3,.3])

Dash code:

html.Div(className="bar-chart",children=[
                    html.H1('Weekly Statistics'),
                    dcc.Dropdown( 
    ['Last Week', 'Last Month', 'Last Year'],
    'Montreal',
    clearable=False,style={'outline':'none','text-decoration':'none','width':'32vw','margin-top':'10px','margin-right':'20px','color':'#28285F','text-align':'right'}
),

dcc.Graph(
    style = {
        'display':'flex',
        'flex-direction':'column',
        'align-items':'flex-start',
        'position':'absolute',
        'width':'40vw',
        'height':'20vw',
        'margin-left':'5vw',
        'margin-top':'6vw',
        'background':'rgba(85, 190, 155, 0.1)'
    },
        id='example-graph',
        figure=fig
    ),
                ]),

Solution

  • You can choose to position the tooltip at the top, right (default), bottom, or left, and I have applied the official tooltip example to your code. I have set the initial graph I create to have no hover association. I receive hover point information from the user and display it in a box. By default, the hover information is displayed after the loading display is displayed since it is a callback from the user. I am a novice in Dash and have taken most of my information from the official reference.

    from dash import Dash, dcc, html, Input, Output, no_update
    #from jupyter_dash import JupyterDash
    import plotly.express as px
    import pandas as pd
    import time
    
    df = pd.DataFrame({
        "Day": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"],
        "Amount": [4, 1, 2, 2, 4, 5,8],
    })
    
    fig = px.bar(df, x="Day", y="Amount",
                 orientation="v",
                 opacity=0.9,
                 barmode='relative',
                 color_discrete_sequence=["purple"],
                 text='Amount',
                 hover_data=None,
                 template='none')
    
    fig.update_traces(texttemplate='%{text:.2s}',
                      hovertemplate=None,
                      hoverinfo='none',
                      textposition='outside',
                      width = [.3,.3,.3,.3,.3,.3,.3]
                     )
    fig.update_layout(yaxis=dict(range=[0,10]))
    #fig.show()
    
    app = Dash(__name__)
    #app = JupyterDash(__name__)
    
    app.layout = html.Div(
        className="container",
        children=[
            dcc.Graph(
                id="graph-4",
                figure=fig,
                clear_on_unhover=True),
            dcc.Tooltip(
                id="graph-tooltip-4",
                loading_text="LOADING",
                direction="top"),
        ],
    )
    
    # This callback is executed very quickly
    app.clientside_callback(
        """
        function show_tooltip(hoverData) {
            if(!hoverData) {
                return [false, dash_clientside.no_update];
            }
            var pt = hoverData.points[0];
            return [true, pt.bbox];
        }
        """,
        Output("graph-tooltip-4", "show"),
        Output("graph-tooltip-4", "bbox"),
        Input("graph-4", "hoverData"),
    )
    
    # This callback is executed after 1s to simulate a long-running process
    @app.callback(
        Output("graph-tooltip-4", "children"),
        Input("graph-4", "hoverData"),
    )
    def update_tooltip_content(hoverData):
        if hoverData is None:
            return no_update
    
        time.sleep(1)
    
        hover_data = hoverData["points"][0]
    
        # Display the x0 and y0 coordinate
        #bbox = hoverData["points"][0]["bbox"]
        y = hover_data['y']
        x = hover_data['x']
        # print('x:', x, 'y:',y)
        return [
            html.P(f"Day={x}, Amount={y}"),
        ]
    
    if __name__ == "__main__":
        app.run_server(debug=True)#, mode='inline'
    

    enter image description here