Revolucion for Monica
Revolucion for Monica

Reputation: 3296

Create a modal when clicking on barchart with dash plotly

I have a bar chart and I would like to create a Modal when I click on them. Do you know how I can handle that? I tried something but nothing appears.

enter image description here

I knew how to do it with a datatable. But here I have a bar chart, I added a Modal object at the end of the design but I can't make it appear despite.

Here is a minimal reproducible example:

import dash
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
from dash.dependencies import Input, Output

df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/salesfunnel.xlsx?raw=True")
pv = pd.pivot_table(df, index=['Name'], columns=["Status"], values=['Quantity'], aggfunc=sum, fill_value=0)

trace1 = go.Bar(x=pv.index, y=pv[('Quantity', 'declined')], name='Declined')
trace2 = go.Bar(x=pv.index, y=pv[('Quantity', 'pending')], name='Pending')
trace3 = go.Bar(x=pv.index, y=pv[('Quantity', 'presented')], name='Presented')
trace4 = go.Bar(x=pv.index, y=pv[('Quantity', 'won')], name='Won')

app = dash.Dash()

app.layout = html.Div(children=[
    html.H1(children='Sales Funnel Report'),
    html.Div(children='''National Sales Funnel Report.'''),
    dcc.Graph(
        id='graph',
        figure={
            'data': [trace1, trace2, trace3, trace4],
            'layout':
                go.Layout(title='Order Status by Customer', barmode='stack')
        }),
    dbc.Modal(
        [
            dbc.ModalHeader("Header"),
            dbc.ModalBody("This is the content of the modal"),
            dbc.ModalFooter(
                dbc.Button("Close", id="close", className="ml-auto")
            ),
        ],
        size="xl",
        id="modal",
    )
])


@app.callback(
    Output('modal', 'children'),
    [Input('graph', 'clickData')])
def display_click_data(clickData):
    print("clickData: ", clickData)
    return [
        dbc.ModalHeader("Test"),
        dbc.ModalBody(
            html.Div([
                html.Div([
                    html.H6('Sales', style={'textAlign': 'center', 'padding': 10}),
                    html.P("Bitch", id="sales_stocks", style={'textAlign': 'center', 'padding': 10})
                ], className='pretty_container four columns'),
                html.Div([
                    html.H5('Current ratio', style={'textAlign': 'center', 'padding': 10})
                ], className='pretty_container seven columns')
            ])),
        dbc.ModalFooter(dbc.Button("Close", id="close"))
    ]


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    app.run_server(debug=True)

Upvotes: 0

Views: 3405

Answers (1)

timovp
timovp

Reputation: 41

I encountered a similar question, but conceptually the same: click on something in a plotly/dash-object and then display a modal. Researching the issue, I also found this post on the plotly forum, which encounters very similar problems. The proposed solution there gave me the idea on how to solve.

There are 2 issues at hand if I copy your code.

  1. its missing the link to the bootstrap "external stylesheet" which can be easily added in your sample above, replacing app = dash.Dash() with app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
  2. the call back now only alters the modal's children. However, to be displayed, it's attribute, open should be set to True. When the button, with id=close is pressed, the open be altered to False.

I worked your code snipped out into something working. Hope this helps you out as well!

import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
# added State to be used in callbacks
from dash.dependencies import Input, Output, State

# using #type: ignore to suppress warnings in my editor (vs code). 
df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/salesfunnel.xlsx?raw=True")
pv = pd.pivot_table(df, index=['Name'], columns=["Status"], values=['Quantity'], aggfunc=sum, fill_value=0)#type: ignore

trace1 = go.Bar(x=pv.index, y=pv[('Quantity', 'declined')], name='Declined') #type: ignore
trace2 = go.Bar(x=pv.index, y=pv[('Quantity', 'pending')], name='Pending')#type: ignore
trace3 = go.Bar(x=pv.index, y=pv[('Quantity', 'presented')], name='Presented')#type: ignore
trace4 = go.Bar(x=pv.index, y=pv[('Quantity', 'won')], name='Won')#type: ignore

app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div(children=[
    html.H1(children='Sales Funnel Report'),
    html.Div(children='''National Sales Funnel Report.'''),
    dcc.Graph(
        id='graph',
        figure={
            'data': [trace1, trace2, trace3, trace4],
            'layout':
                go.Layout(title='Order Status by Customer', barmode='stack')#type: ignore
        }),
    dbc.Button("toggle", id="open"),
    dbc.Modal(
        [
            dbc.ModalHeader("Header",id = 'modalheader'),
            dbc.ModalBody("This is the content of the modal"),
            dbc.ModalFooter(
                dbc.Button("Close", id="close", className="ml-auto")
            ),
        ],
        size="xl",
        id="modal",
    )
])

@app.callback([Output("modal", "children"),
               Output("modal", "is_open")],
              [Input("graph", "clickData"),
               Input("close", "n_clicks")],
              [State("modal", "is_open"),
               State("modal", "children")])
def set_content(value,clicked,is_open,children):
    ctx = dash.callback_context

    if ctx.triggered[0]['prop_id'] == 'close.n_clicks':
        # you pressed the closed button, keeping the modal children as is, and 
        # close the model itself. 
        return children, False 
    elif ctx.triggered[0]['prop_id'] == 'graph.clickData':
        # you clicked in the graph, returning the modal children and opening it
        return [dbc.ModalHeader("Test"),
                dbc.ModalBody(
                html.Div([
                    html.Div([
                        html.H6('Sales', style={'textAlign': 'center', 'padding': 10}),
                        html.P("Bitch", id="sales_stocks", style={'textAlign': 'center', 'padding': 10})
                    ], className='pretty_container four columns'),
                    html.Div([
                        html.H5('Current ratio', style={'textAlign': 'center', 'padding': 10}),
                        html.P(str(value),style = {'fontFamily':'monospace'})
                    ], className='pretty_container seven columns')
                ])),
                dbc.ModalFooter(dbc.Button("Close", id="close"))
                ], True
    else:
        raise dash.exceptions.PreventUpdate

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    app.run_server(debug=True)

Upvotes: 2

Related Questions