New to dash and plotly and frontend development, need help

Hi,

I am new to plotly dash and web development as well. I am trying to show a funnell graph on the click of a button. I have written some code. Unfrotunately the page gets unresponsive immediately after loading.
When the user clicks on th Submit button i want to show the text entered into the text area as well as show a funnell graph with the length of the sentences entered aftered splitting them on period.

Here is what I have so far:
The formatting for the code is messed up, but the code starts here:

----------------------app.py code starts here ---------------------

from dash import Dash, html, dcc
from dash.dependencies import Input, Output, State
import plotly.express as px


external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = Dash(__name__,external_stylesheets=external_stylesheets,title="Sample")

colors = {
    'background_1': '#111111',
    'background_2': '#FFFFFF',
    'text_1' : '#7FDBFF',
    'text_2' : '#FFFFFF',
    'text_3' : '#ff1493',
    'text_4' : '#111111'

}

app.layout = html.Div(style={'backgroundColor':colors['background_2']},children=[
    html.Div(
        className="app-header",
        children=[
            html.Div('Sample',className="app-header--title")
        ]
    ),
    html.Div(
        children=html.Div([
            html.H5('Overview'),
            html.Div(style={'color':colors['text_4']},children=['''
            Enter text into the Text box below and click Submit:
            '''])
        ])
    ),
    html.Br(),
    dcc.Textarea(
        id='textarea-state-example',
        value='Enter your text here:',
        style={'width': '100%', 'height': 200},
    ),

    html.Br(),
    html.Div(children=[html.Button('Submit', id='textarea-state-example-button', n_clicks=0)],
             style={'textAlign':'center','color':colors['text_3']}   
    ),
    html.Br(),
    html.Br(),

    html.Div(id='textarea-state-example-output', style={'whiteSpace': 'pre-line'}),
    html.Br(),
    html.Br(),
    html.Br(),
    ## ***************I think this is the problem*********
    ## The web page becomes unresponsive
    html.Div([dcc.Graph(id='lengths_plot')]),
    html.Br(),
    html.Br(),
    html.Br(),
    html.Div(
        children=html.Div([
            html.H5('Summary'),
            html.Div('''
            The total text entered:
            ''')
        ])
    ),

    html.Br(),
    html.Br(),
    html.Br(),

        html.Div(
        className="app-footer",
        children=[
            html.Div('Hi!'),
        ]
    ),


])



@app.callback(
    Output(component_id='lengths_plot', component_property= 'figure'),
    Input('textarea-state-example-button', 'n_clicks'),
    State('textarea-state-example', 'value')
)
def update_graph(n_clicks,value):
    if n_clicks > 0:
        input_sentences = value.split(".")
        input_sentences = [sent.strip() for sent in input_sentences if len(sent.strip()) > 0]

        data_dict = {}
        for sent in input_sentences:
            data_dict[sent] = len(sent)

        data = {}
        data['sentences'] = list(data_dict.keys())
        data['length'] = list(data_dict.values())

        # I am just using this as a test i know this
        # is not a full funnel graph. bit just for testing
        # i will improve on this once i can get it working
        fig = px.funnel(data,x='sentences',y='length', color='length', labels={"length":"Length"})
        fig.update_layout(title = 'Sentence Lengths',
                              xaxis_title = 'Sentences',
                              yaxis_title = 'Lengths'
                            )

        return fig

@app.callback(
    Output('textarea-state-example-output', 'children'),
    Input('textarea-state-example-button', 'n_clicks'),
    State('textarea-state-example', 'value')
)
def update_data(n_clicks, value):
    if n_clicks > 0:
        # get the len of
        # the input value

        # for now just return 
        # the text and populate
        # in the output text
        # area

        return "You have entered:\n{}".format(value)


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


--------app.py code ends here-----------------

The following files go into the assets folder.

footer.css

.app-footer {
    height: 60px;
    line-height: 60px;
    border-top: thick burlywood solid;
    border-bottom: thick burlywood solid;
    text-align: center;
}

.app-footer .app-footer--title {
    font-size: 14px;
    padding-left: 5px;
    /* color: white; */
    color: black;
    
}

header.css

.app-header {
    height: 60px;
    line-height: 60px;
    border-bottom: thin lightgrey solid;
    text-align: center;
}

.app-header .app-header--title {
    font-size: 22px;
    padding-left: 5px;
    /* color: white; */
    color: black;
    
}

typography.css

body {
    font-family: sans-serif;
}
h1, h2, h3, h4, h5, h6 {
    color: orange
}

I am using the standard typoraphy.css,header.css and custom-script.js from the tutorial at Adding CSS & JS and Overriding the Page-Load Template | Dash for Python Documentation | Plotly

Any help is greatly appreciated.

Thanks

Any help is greatly appreciated. Thanks

HI @mariasmo54, could you please format your code for better readability?

Hi AIMPED,

Thanks for the suggestion. Made the changes and the code is now well formatted.

HI @mariasmo54 while it does not solve your problem, it narrows down the code to debug. The commented out lines are the culprit of your observation.

from dash import Dash, html, dcc
from dash.dependencies import Input, Output, State
import plotly.express as px

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = Dash(__name__, external_stylesheets=external_stylesheets, title="Sample")

colors = {
    'background_1': '#111111',
    'background_2': '#FFFFFF',
    'text_1': '#7FDBFF',
    'text_2': '#FFFFFF',
    'text_3': '#ff1493',
    'text_4': '#111111'

}

app.layout = html.Div(style={'backgroundColor': colors['background_2']}, children=[
    html.Div(
        className="app-header",
        children=[
            html.Div('Sample', className="app-header--title")
        ]
    ),
    html.Div(
        children=html.Div([
            html.H5('Overview'),
            html.Div(style={'color': colors['text_4']}, children=['''
            Enter text into the Text box below and click Submit:
            '''])
        ])
    ),
    html.Br(),
    dcc.Textarea(
        id='textarea-state-example',
        value='Enter your text here:',
        style={'width': '100%', 'height': 200},
    ),

    html.Br(),
    html.Div(children=[html.Button('Submit', id='textarea-state-example-button', n_clicks=0)],
             style={'textAlign': 'center', 'color': colors['text_3']}
             ),
    html.Br(),
    html.Br(),

    html.Div(id='textarea-state-example-output', style={'whiteSpace': 'pre-line'}),
    html.Br(),
    html.Br(),
    html.Br(),
    ## ***************I think this is the problem*********
    ## The web page becomes unresponsive
    html.Div([dcc.Graph(id='lengths_plot')]),
    html.Br(),
    html.Br(),
    html.Br(),
    html.Div(
        children=html.Div([
            html.H5('Summary'),
            html.Div('''
            The total text entered:
            ''')
        ])
    ),

    html.Br(),
    html.Br(),
    html.Br(),

    html.Div(
        className="app-footer",
        children=[
            html.Div('Hi!'),
        ]
    ),

])


@app.callback(
    Output(component_id='lengths_plot', component_property='figure'),
    Input('textarea-state-example-button', 'n_clicks'),
    State('textarea-state-example', 'value'),
    prevent_initial_call=True
)
def update_graph(_, value):
    # input_sentences = value.split(".")
    # input_sentences = [sent.strip() for sent in input_sentences if len(sent.strip()) > 0]
    #
    # data_dict = {}
    # for sent in input_sentences:
    #     data_dict[sent] = len(sent)
    #
    # data = {}
    # data['sentences'] = list(data_dict.keys())
    # data['length'] = list(data_dict.values())
    #
    # # I am just using this as a test i know this
    # # is not a full funnel graph. bit just for testing
    # # i will improve on this once i can get it working
    # fig = px.funnel(data, x='sentences', y='length', color='length',
    #                 labels={"length": "Length"})
    # fig.update_layout(title='Sentence Lengths',
    #                   xaxis_title='Sentences',
    #                   yaxis_title='Lengths'
    #                   )

    return px.scatter(x=[1, 2, 3], y=[1, 2, 3])


@app.callback(
    Output('textarea-state-example-output', 'children'),
    Input('textarea-state-example-button', 'n_clicks'),
    State('textarea-state-example', 'value'),
    prevent_initial_call=True
)
def update_data(_, value):
    # get the len of
    # the input value

    # for now just return
    # the text and populate
    # in the output text
    # area

    return "You have entered:\n{}".format(value)


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

Hi AIMPED,

THanks for the quick response.

I will try it out

But could it be the following code which is the actual culprit?

# fig = px.funnel(data, x='sentences', y='length', color='length',
    #                 labels={"length": "Length"})
    # fig.update_layout(title='Sentence Lengths',
    #                   xaxis_title='Sentences',
    #                   yaxis_title='Lengths'
    #                   )

How can I use a funnel chart and update it on the click of the button?

Any suggestions?

Thanks

Hi,

It is absolutely possible to update charts on a button click. The button callback works right now, you’ll see your input in the textarea-state-example-output

I actually never have used funnel charts, here is the documentation for them:

Thank you. I will look into it.

So when I looked at the code you modofied, its basically returning a static plot.

I was reading somewhere that dash tries to run the callbacks on load as well. Is it possible that since I had my code in a conditional like:

if n_clicks > 0:

the condition is not met and it keeps trying to load the funnell chart and becomes unresponsive? If so what should I send as a default?

say like this:

if n_clicks > 0:
    some update code
    return fig

return some_default_fig

could that be issue?

I prevented the initial call at app startup by using prevent_initial_call=True

I’m not sure about the funnel since I could not test ist due to the lack of data. Since you are trying to build the funnel chart on some text input, I did not know the format of this input. If you gave me an example test input, I could take a look at this tonight at some point.

Hi AIMPED,

Thank you so much. The input could be any paragraph of text.

For example take your earlier reply as input. Here it is:

sample input:

“”"
I’m not sure about the funnel since I could not test ist due to the lack of data. Since you are trying to build the funnel chart on some text input. I did not know the format of this input. If you gave me an example test input, I could take a look at this tonight at some point.
“”"

Copy and paste the above four sentences into the text area and hit submit.

Thanks for your help.

Please try it with this (basically the code snippet from above without commenting out your code). It does not throw any error.

from dash.dependencies import Input, Output, State
import plotly.express as px

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = Dash(__name__, external_stylesheets=external_stylesheets, title="Sample")

colors = {
    'background_1': '#111111',
    'background_2': '#FFFFFF',
    'text_1': '#7FDBFF',
    'text_2': '#FFFFFF',
    'text_3': '#ff1493',
    'text_4': '#111111'

}

app.layout = html.Div(style={'backgroundColor': colors['background_2']}, children=[
    html.Div(
        className="app-header",
        children=[
            html.Div('Sample', className="app-header--title")
        ]
    ),
    html.Div(
        children=html.Div([
            html.H5('Overview'),
            html.Div(style={'color': colors['text_4']}, children=['''
            Enter text into the Text box below and click Submit:
            '''])
        ])
    ),
    html.Br(),
    dcc.Textarea(
        id='textarea-state-example',
        value='Enter your text here:',
        style={'width': '100%', 'height': 200},
    ),

    html.Br(),
    html.Div(children=[html.Button('Submit', id='textarea-state-example-button', n_clicks=0)],
             style={'textAlign': 'center', 'color': colors['text_3']}
             ),
    html.Br(),
    html.Br(),

    html.Div(id='textarea-state-example-output', style={'whiteSpace': 'pre-line'}),
    html.Br(),
    html.Br(),
    html.Br(),
    ## ***************I think this is the problem*********
    ## The web page becomes unresponsive
    html.Div([dcc.Graph(id='lengths_plot')]),
    html.Br(),
    html.Br(),
    html.Br(),
    html.Div(
        children=html.Div([
            html.H5('Summary'),
            html.Div('''
            The total text entered:
            ''')
        ])
    ),

    html.Br(),
    html.Br(),
    html.Br(),

    html.Div(
        className="app-footer",
        children=[
            html.Div('Hi!'),
        ]
    ),

])


@app.callback(
    Output(component_id='lengths_plot', component_property='figure'),
    Input('textarea-state-example-button', 'n_clicks'),
    State('textarea-state-example', 'value'),
    prevent_initial_call=True
)
def update_graph(_, value):
    input_sentences = value.split(".")
    input_sentences = [sent.strip() for sent in input_sentences if len(sent.strip()) > 0]

    data_dict = {}
    for sent in input_sentences:
        data_dict[sent] = len(sent)

    data = {}
    data['sentences'] = list(data_dict.keys())
    data['length'] = list(data_dict.values())

    # I am just using this as a test i know this
    # is not a full funnel graph. bit just for testing
    # i will improve on this once i can get it working
    fig = px.funnel(data, x='sentences', y='length', color='length',
                    labels={"length": "Length"})
    fig.update_layout(title='Sentence Lengths',
                      xaxis_title='Sentences',
                      yaxis_title='Lengths'
                      )
    return fig


@app.callback(
    Output('textarea-state-example-output', 'children'),
    Input('textarea-state-example-button', 'n_clicks'),
    State('textarea-state-example', 'value'),
    prevent_initial_call=True
)
def update_data(_, value):
    # get the len of
    # the input value

    # for now just return
    # the text and populate
    # in the output text
    # area

    return "You have entered:\n{}".format(value)


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

Hi AIMPED,

Thank you. That seems to have fixed the issue. I will test it further and post here my findings.

My question for you and for the wider community:

How to solve the issue that I was seeing when I put the fig update in a conditional line

if n_clicks > 0:

There should be a way to send a default graph figure when the condtion is not met. Essentially i want to update the graph only when the Submit button is clicked. Some deafault fig when the page is iinitially loaded.

Any thoughts?

Thanks once more for all your help.

Hi @mariasmo54 you can use the conditional, that’s possible. Looking at your initial code again, there is only one small piece of code missing in order to make it work.

Your callback function does not return anything if the condition if n_clicks > 0 returns False which is the case at startup. So adding a return statement with a default figure makes your app work without any further changes.

Sorry for not seeing that in the beginning.

def update_graph(n_clicks,value):
    if n_clicks > 0:
        input_sentences = value.split(".")
        input_sentences = [sent.strip() for sent in input_sentences if len(sent.strip()) > 0]

        data_dict = {}
        for sent in input_sentences:
            data_dict[sent] = len(sent)

        data = {}
        data['sentences'] = list(data_dict.keys())
        data['length'] = list(data_dict.values())

        # I am just using this as a test i know this
        # is not a full funnel graph. bit just for testing
        # i will improve on this once i can get it working
        fig = px.funnel(data,x='sentences',y='length', color='length', labels={"length":"Length"})
        fig.update_layout(title = 'Sentence Lengths',
                              xaxis_title = 'Sentences',
                              yaxis_title = 'Lengths'
                            )

        return fig
      return px.scatter(x=[1,2,3], y=[1,2,3])

There is another posibility to show an initial figure at startup, using the figure argument of the dcc.Graph() and assign the initial figure

in your case:

 html.Div(
    [
        dcc.Graph(
            id='lengths_plot'
            figure=px.scatter(x=[1, 2, 3], y=[3, 2, 5])
        )
    ]
)

Hi AIMPED,

Thank you. I will give it a try ( i will try to show a default funnel chart instead of a scatter though).

So i did give a default value yeserday for the false condition as

return None

and it was unresponsive as well.

I should have returned a valid graph object instead. i will try these options and let you know my findings.

Thank you so much.

1 Like

You can just return an empty dictionary instead of None, this should work. {}

Thank you so much. The empty dictionary is working.

Hi AIMPED,

One more question. Is it possible to return multiple values in the update graph function?

like:

return fig, some_float_value

can i do that?

Hello @mariasmo54,

Yes, you can do that. You just need to give two output targets in your callback. :grin:

There is a ton of customization that you can accomplish via callbacks.

Yes. Got it working. Thank you.

1 Like