Hello Dash users,
Trying to build a multi-page app based on the Vanguard report style (https://github.com/plotly/dash-vanguard-report/blob/master/app.py). It looks very suitable to my needs! However, in the example each page is static and is completely declared in the separate layouts. In my case, I need to build graphs which are dynamically populated by callbacks. Everything works fine except one problem: when I’m in a single subpage, which for example looks like this:
overview = html.Div([ # page 1
html.Div([
# Header
get_logo(),
get_header(),
html.Br([]),
get_input(),
get_result_bar(),
get_menu(),
# Row 2
html.Div([
html.Div([
dcc.Graph(id='sentiment-overview'),
# html.Div(id='topic-content')
# html.Div(DataTable(rows=[{}]), style={'display': 'none'})
], className='twelve columns padded')
],
className='row ')
], className="subpage")
], className="page")
and the get_menu()
looks like this:
def get_menu():
menu = html.Div([
dcc.Link('Overview ', href='/overview', className="tab first"),
dcc.Link('Sentiment timeline ', href='/timeline', className="tab"),
dcc.Link('Sentiment dynamic ', href='/dynamic', className="tab")
], className="row ")
return menu
the following callback needs to be fired to be able to build a graph from a intermediate hidden div with data:
@app.callback(
dash.dependencies.Output('sentiment-overview', 'figure'),
[dash.dependencies.Input('url', 'pathname'), Input('intermediate-value-df', 'children')])
def sentiment_overview(pathname, jsonified_data):
print("Pathname in overview graph:", pathname)
if (pathname == '/overview' or pathname == "/") and jsonified_data:
figure = {
'data': [
go.Bar(x=positive['Day'], y=positive['count'], name='positive',
marker=dict(color='#1ABC9C')),
go.Bar(x=negative['Day'], y=negative['count'], name='negative',
marker=dict(color='#E74C3C'))
],
'layout': go.Layout(
title='Number of pos/neg tweets per day',
showlegend=True
)
}
return figure
In the second page, which looks similarly:
sentimentTimeline = html.Div([ # page 2
html.Div([
# Header
get_logo(),
get_header(),
html.Br([]),
get_input(),
get_result_bar(),
get_menu(),
# Row 2
html.Div([
html.Div([
dcc.Graph(id='sentiment-timeline'),
], className="twelve columns padded")
], className="row "),
], className="subpage")
], className="page")
the needed callback is placed right after the first one:
@app.callback(
dash.dependencies.Output('sentiment-timeline', 'figure'),
[dash.dependencies.Input('url', 'pathname'),
Input('intermediate-value-df', 'children')])
def sentiment_timeline(pathname, jsonified_data):
print("Pathname in timeline graph:", pathname)
if (pathname == '/timeline') and jsonified_data:
sentiment_df = pd.read_json(jsonified_data)
# print(sentiment_df.head(1))
positive = sentiment_df.loc[sentiment_df['DateValue'] > 0.05]
negative = sentiment_df.loc[sentiment_df['DateValue'] < -0.05]
figure = {'data': [
go.Scatter(
x=positive.Date,
y=signal.savgol_filter(positive.DateValue, 21, 3),
customdata=positive.Tweet,
name="Positive tweets",
line=dict(color='#1ABC9C'),
opacity=0.8),
go.Scatter(
x=negative.Date,
y=signal.savgol_filter(negative.DateValue, 21, 3),
customdata=negative.Tweet,
name="Negative tweets",
line=dict(color='#E74C3C'),
opacity=0.8)],
'layout': go.Layout(
title='Sentiment over time: 1 - positive, -1 - negative',
xaxis=dict(
rangeselector=dict(
buttons=list([
dict(count=1,
label='1d',
step='day',
stepmode='todate'),
dict(count=1,
label='1h',
step='hour',
stepmode='backward'),
dict(count=6,
label='6h',
step='hour',
stepmode='backward')
])
),
rangeslider=dict(
visible=True
),
type='date'
)
)}
return figure
These two callbacks seem to block each other in some way because whenever I am in the first page, I need to click the menu item twice to be able to properly see the loaded graph. The same goes for the second page.
Also note that with the debug print statements seem to show that when I’m on page 2, the first callback gets triggered until I click on the link for the second page for the second time, only then the right callback gets triggered.
My app Layout is:
app.layout = html.Div([
dcc.Location(id='url'),
html.Div(id='handle-value', style={'display': 'none'}),
html.Div(id='intermediate-value', style={'display': 'none'}),
html.Div(id='intermediate-value-df', style={'display': 'none'}),
html.Div(id='page-content')
])
and the display function is:
# Update page
@app.callback(dash.dependencies.Output('page-content', 'children'),
[dash.dependencies.Input('url', 'pathname'), dash.dependencies.Input('intermediate-value', 'children')])
def display_page(pathname, value):
if (pathname == '/overview' or pathname == "/") and value:
print("in the overview display page")
return overview
elif pathname == '/timeline' and value:
print("In the timeline display page")
return sentimentTimeline
else:
return empty_template
I attached a small gif to demonstrate the problem (don’t mind the messages in between or bad graph, it’s just for debug ). It’s a long one so I am happy to show more of the code if necessary!
Thanks a lot in advance!!
dash|600x400