Black Lives Matter. Please consider donating to Black Girls Code today.

Graph state resets after page refresh & size changes when extending graph (SQLALCHEMY)

Hello,

I am trying to add a feature where I have several input components which will be added to a sqlalchemy database and update a graph based on calculations of that input. Everything works but my problem is that if I refresh the page the graph is set back to where it was when I ran the server.

Only after I shut down and re-run the server the graph is updated with the new inputs. Is it possible that a I can refresh the page and keep the state of the graph from the current session?

Another problem is that when I give input to the graph and update it, the size of the graph shrinks. Is there a way to fix the graph size so id doesn’t change even if I add more data points? I tryed to set the height but that doesn’t prevent the graph from shrinking when I update it with new data points.

Layout:

dbc.Row(
	[
		dbc.Col(html.Div(dcc.Graph(id='bmi-graph',
				figure={'data':[

						go.Scatter(y=[i[0] for i in BMI.query.with_entities(BMI.bmi_val).all()])

				],'layout':go.Layout(
												xaxis = dict(showgrid=False,showticklabels=False,zeroline=False),
												yaxis = dict(title='BMI',showgrid=False,showticklabels=False),
												hovermode='closest',
												template = "plotly_white",
												height=300,
												margin = go.layout.Margin(t=0)
												#width=490,
																)},

												config={
														'displayModeBar': False
														})),width=4),
		dbc.Col(html.Div(
												html.Div([
												html.Div(dcc.Input(id='input-height', type='number', debounce=True, min=1, step=1, placeholder=f"{height} cm")),
												html.Div(dcc.Input(id='input-weight', type='number', debounce=True, min=1, step=1, placeholder=f"{weight} kg")),
												dbc.Button('calculate bmi',id='button',color='dark',className='mr-1',style={'marginTop':'5px'}),
												#html.Button('Calculate BMI', id='button'),
												html.Div(id='bmi-alert',style={'width':'200px'})
														])
					),width=2)])

callback:

@app.callback(Output('bmi-graph','figure'),
                [Input('button','n_clicks')],
                [State('input-height','value'),
                State('input-weight','value')])

def update_graph(n_clicks,height,weight):

    bmi = round(float(weight)/((float(height)/100)**2))

    user = BMI(bmi_val=bmi)
    db.session.add(user)
    db.session.commit()

    query = BMI.query.with_entities(BMI.bmi_val).all()
    bmis = [i[0] for i in query]

    figure = {'data':[

            go.Scatter(y=bmis),

        ],'layout':go.Layout(
                    xaxis = dict(showgrid=False,showticklabels=False,zeroline=False),
                    yaxis = dict(title='BMI',showgrid=False,showline=False),
                    hovermode='closest',
                    template = "plotly_white",
                    height=300
        )}

    return figure

Would be glad to get some help!

The size should stay fixed if you provide both height and width, does that work? Providing just one of them doesn’t work.

Now to your main question, I suspect what’s happening is:

  • Your layout is a constant, so when the page is loaded you get whatever the query yielded when the server was started.
  • Your callback fails its initial call, when height and weight are None. (if that’s what’s happening you should see an error in the server logs, or if you run with debug=True you’ll see that in the browser)

Two ways you could solve this:

  1. Make the layout into a function - that way it is re-evaluated with every request for the page. But even better:
  2. Remove the figure from the initial layout entirely, just dcc.Graph(id='bmi-graph', config={'displayModeBar': False}), then fix the callback by putting the whole “new user” part into an if n_clicks: clause (also maybe protect against missing height and weight):
def update_graph(n_clicks,height,weight):

    if n_clicks and height and weight:
        bmi = round(float(weight)/((float(height)/100)**2))

        user = BMI(bmi_val=bmi)
        db.session.add(user)
        db.session.commit()

    query = BMI.query.with_entities(BMI.bmi_val).all()
    ...

Hey, thank you for replying!

Following your second point worked perfectly (even solved the sizing problem without having to add height and width). But I had to keep in the initial layout otherwise the graph would be empty until triggering a new n_clicks after refreshing the page.

Thanks!