Related
I am trying to use the hoverData of a plot with many traces to display a side table of values related to each trace. The main code runs as follows. (note this is not the full code, but i included the relevant info)
def plots(self,):
df_lists = self.df_lists
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_average', 'p_repitition_average', 'p_median','p_median_all', 'p_median_average','p_range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
from dash import Dash, dcc, html, Input, Output, State
names = self.names
app = Dash()
app.layout = html.Div( children=[
html.H4('Dieharder Tests Plots'),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
dcc.Graph(id="plot", style={'width':'75%', 'float': 'left','height': '70vh','display':'inline-block'}),
html.Div([dcc.Graph(id='hover-data', style ={'float':'right'})], style={'width':'20%', 'paddingTop':35}),
])
#app.callback(
Output("plot", "figure"),
[Input("plot-picker", "value"), Input("test-picker", "value")])
def update_bar_chart(plot_picker, picker_test):
i=0
if plot_picker == 'weakness':
data = []
for test in picker_test:
df = df_lists[test]
p_value = [x for x in df.columns if x.startswith('pva')]
n_rounds = len(p_value)
trace = go.Bar(x=df.test_name, y = df.weak_rate, name = '{}, #rounds: {}'.format(test,n_rounds))
data.append(trace)
layout = go.Layout(title = 'Fraction of weak and failed results per each Dieharder test')
fig = go.Figure(data, layout)
fig.update_yaxes(title_text='Failed/weak fractions')
fig.update_layout(legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01))
return fig
The hover data includes the number of the trace not its name, which i need to specify the df source of the data. I am using the following code to get the hover data to generate the table:
#app.callback(Output('hover-data', 'graph'),
[Input('plot', 'hoverData')] )
def hover_data(hoverData):
Die_test = hoverData['points'][0]['x']
curve_number = hoverData['points'][0]['curveNumber']
trace_name = app.layout['plot'].figure['data'][curve_number]['name']
df = df_lists[trace_name]
df = df[df['test_name'] == Die_test]
data = [go.Table(header=dict(values=['p_mean', 'p_median', 'range', 'std'], fill_color='paleturquoise', align='left'), cells=dict(values=[df['p_mean'], df['p_median'], df['range'], df['std']] ))]
fig = go.Figure(data,)
return fig
The problem it is not working. I am not seeing anything when i hover over the data. I am not sure where the problem is coming, but most probably from the trace_name variable as i am getting the error:
Callback error updating hover-data.graph
AttributeError: 'Graph' object has no attribute 'figure'.
I tried to include a [State('plot', 'figure')] in the input of the callback. and then use the .figure['data'][curve_number]['name'] directly (instead of using app.layout['plot'] first), but it also didn't work.
Any help is appreciated.
Thanks
I don't have your dataframe so I think you can refer my code to revise yours:
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
import dash_bootstrap_components as dbc
app = Dash(__name__, external_stylesheets=[dbc.themes.LUX])
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
app.layout = html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(
df['Indicator Name'].unique(),
'Fertility rate, total (births per woman)',
id='crossfilter-xaxis-column',
),
dcc.RadioItems(
['Linear', 'Log'],
'Linear',
id='crossfilter-xaxis-type',
labelStyle={'display': 'inline-block', 'marginTop': '5px'}
)
], width={'size': 6, "offset": 0, 'order': 1}),
dbc.Col([
dcc.Dropdown(
df['Indicator Name'].unique(),
'Life expectancy at birth, total (years)',
id='crossfilter-yaxis-column'
),
dcc.RadioItems(
['Linear', 'Log'],
'Linear',
id='crossfilter-yaxis-type',
labelStyle={'display': 'inline-block', 'marginTop': '5px'}
)
], width={'size': 6, "offset": 0, 'order': 1})
], style={'padding': '10px 5px'}, className='p-2 align-items-center'),
dbc.Row([
dbc.Col([
dcc.Graph(
id='crossfilter-indicator-scatter',
hoverData={'points': [{'customdata': 'Japan'}]}
)
], width={'size': 6, "offset": 0, 'order': 1}),
dbc.Col([
dash_table.DataTable(id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=[],
style_table={'height': 550},
style_header={'backgroundColor': 'orange', 'padding': '10px', 'color': '#000000'},
style_cell={'textAlign': 'center', 'font_size': '12px',
'whiteSpace': 'normal', 'height': 'auto'},
editable=True, # allow editing of data inside all cells
filter_action="native", # allow filtering of data by user ('native') or not ('none)
sort_action="native", # enables data to be sorted per-column by user or not ('none')
sort_mode="single", # sort across 'multi' or 'single' columns
column_selectable="multi", # allow users to select 'multi' or 'single' columns
row_selectable="multi", # allow users to select 'multi' or 'single' rows
row_deletable=True, # choose if user can delete a row (True) or not (False)
selected_columns=[], # ids of columns that user selects
selected_rows=[], # indices of rows that user selects
page_action="native",
export_headers='display')
], width={'size': 6, "offset": 0, 'order': 1}),
], className='p-2 align-items-center'),
dbc.Row([
dbc.Col([
dcc.Slider(
df['Year'].min(),
df['Year'].max(),
step=None,
id='crossfilter-year--slider',
value=df['Year'].max(),
marks={str(year): str(year) for year in df['Year'].unique()}
)
], width={'size': 6, "offset": 0, 'order': 1})
], className='p-2 align-items-center')
])
#app.callback(
Output('crossfilter-indicator-scatter', 'figure'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-yaxis-column', 'value'),
Input('crossfilter-xaxis-type', 'value'),
Input('crossfilter-yaxis-type', 'value'),
Input('crossfilter-year--slider', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_traces(customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_xaxes(title=xaxis_column_name, type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name, type='linear' if yaxis_type == 'Linear' else 'log')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
return fig
#app.callback(
Output('table', 'data'),
Input('crossfilter-indicator-scatter', 'hoverData'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-xaxis-type', 'value'))
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
country_name = hoverData['points'][0]['customdata']
dff = df[df['Country Name'] == country_name]
dff = dff[dff['Indicator Name'] == xaxis_column_name]
return dff.to_dict(orient='records')
if __name__ == '__main__':
app.run_server(debug=False, port=1414)
I think instead of return go.Table, you can use dash_table in your Div and then return filtered data frame. Hope this help.
I have a dash app that plots a dataframe which has a date component, and an entry that is either true or false. There are two graphs in the dashboard, one with the data vs date, and one with a percentage of True/False like below:
I can zoom in on the date range and select a subset clicking with the mouse.
I would like to feed this range back into the second graph.
At the moment to produce the above dashboard the relevant part of the code looks like:
from re import template
import pandas as pd
import plotly.express as px
from dash import Dash, Input, Output, dcc, html
from flask import globals
def init_dashboard(server):
evicted_df = pd.read_csv("app/data/evicted_jobs_node.csv", sep="\t")
all_df = pd.read_csv("app/data/all_jobs_node.csv", sep="\t")
all_df["datetime"] = pd.to_datetime(all_df["datetime"])
all_df = all_df.set_index(["datetime"])
all_df["evicted"] = all_df["id_job"].isin(evicted_df["id_job"])
app = Dash(__name__, server=server, routes_pathname_prefix="/dash/")
app.layout = html.Div(
[
html.Div(
className="row",
children=[
html.Div(
className="six columns",
children=[dcc.Graph(id="graph-with-dropdown")],
style=dict(width="75%"),
),
html.Div(
className="six columns",
children=[dcc.Graph(id="graph-with-dropdown2")],
style=dict(width="25%"),
),
],
style=dict(display="flex"),
),
html.Div(
className="row",
children=[
html.Div(
className="six columns",
children=[
dcc.Dropdown(
id="partition-dropdown",
options=[
"Partition (default is all)",
*all_df["partition"].unique(),
],
value="Partition (default is all)",
clearable=False,
searchable=False,
)
],
style={
"width": "50%",
"justify-content": "center",
},
),
html.Div(
className="six columns",
children=[
dcc.Dropdown(
id="node-dropdown",
options=[
"Number of Nodes (default is all)",
*sorted(
[
int(nodes)
for nodes in all_df["nodes_alloc"].unique()
]
),
],
value="Number of Nodes (default is all)",
clearable=False,
searchable=False,
)
],
style=dict(width="50%"),
),
],
style=dict(display="flex"),
),
]
)
init_callbacks(app, df, all_df)
return app.server
def init_callbacks(app, df, all_df):
#app.callback(
Output("graph-with-dropdown2", "figure"),
[Input("node-dropdown", "value"), Input("partition-dropdown", "value")],
)
def update_evicted_fig(selected_nodes, selected_partition):
if selected_nodes != "Number of Nodes (default is all)":
filtered_df = all_df[all_df["nodes_alloc"] == selected_nodes]
else:
filtered_df = all_df
if selected_partition != "Partition (default is all)":
filtered_df = filtered_df[filtered_df["partition"] == selected_partition]
x = ["Not Evicted", "Evicted"]
df1 = filtered_df.groupby(["evicted"]).count().reset_index()
fig = px.bar(
df1,
y=[
100
* filtered_df[filtered_df["evicted"] == False].size
/ filtered_df.size,
100
* filtered_df[filtered_df["evicted"] == True].size
/ filtered_df.size,
],
x=x,
color="evicted",
color_discrete_map={True: "red", False: "green"},
labels={"x": "Job Status", "y": "% of Jobs"},
)
fig.update_layout(transition_duration=500)
return fig
#app.callback(
Output("graph-with-dropdown", "figure"),
[Input("node-dropdown", "value"), Input("partition-dropdown", "value")],
)
def update_evicted_fig(selected_nodes, selected_partition):
if selected_nodes != "Number of Nodes (default is all)":
filtered_df = all_df[all_df["nodes_alloc"] == selected_nodes]
else:
filtered_df = all_df
if selected_partition != "Partition (default is all)":
filtered_df = filtered_df[filtered_df["partition"] == selected_partition]
print(
filtered_df[filtered_df["evicted"] == True]
.groupby([pd.Grouper(freq="6H")])
.sum(numeric_only=True)["node_hours"]
)
fig = px.bar(
x=filtered_df[filtered_df["evicted"] == False]
.groupby([pd.Grouper(freq="6H")])
.sum(numeric_only=True)["node_hours"]
.index,
y=filtered_df[filtered_df["evicted"] == False]
.groupby([pd.Grouper(freq="6H")])
.sum(numeric_only=True)["node_hours"],
labels={
"x": "Date",
"y": "Node hours",
},
title="Job Status",
barmode="stack",
)
fig.add_bar(
name="Evicted",
x=filtered_df[filtered_df["evicted"] == True]
.groupby([pd.Grouper(freq="6H")])
.sum(numeric_only=True)["node_hours"]
.index,
y=filtered_df[filtered_df["evicted"] == True]
.groupby([pd.Grouper(freq="6H")])
.sum(numeric_only=True)["node_hours"],
)
fig.update_layout(transition_duration=500)
return fig
return app.server
Is what I am hoping to do possible, and if so is there some documentation or a worked example someone could highlight for me?
I don't have you df so maybe you can refer my code to revise yours:
import pandas as pd
import numpy as np
import plotly.express as px
import dash
import dash_html_components as html
from dash import dcc
from dash_extensions.enrich import Input, Output, State, ServersideOutput
import dash_bootstrap_components as dbc
from dash.exceptions import PreventUpdate
df_2 = df[(df['BAS_DT'] >= '2022-01-01')]
df5 = df_2.pivot_table(values='USD_XC_BL',
index=['BAS_DT'],
aggfunc=np.sum).reset_index()
fig_3 = px.bar(df5,
x='BAS_DT',
y='USD_XC_BL',
labels='BAS_DT',
hover_name='BAS_DT', color_discrete_sequence=px.colors.qualitative.Alphabet)
fig_3.update_layout(xaxis_title="", yaxis_title="", plot_bgcolor='rgba(0,0,0,0)', margin=dict(l=0, r=0, t=0, b=0))
fig_3.update_xaxes(showline=False, showgrid=False),
fig_3.update_yaxes(showline=False, showgrid=False, separatethousands=True, tickformat=',.0f')
app = dash.Dash(__name__)
app.layout = html.Div([
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
dbc.Row([
dbc.Col([
html.H5('Amount by Currency', style={"text-align": "center"}),
dcc.Loading(children=[dcc.Graph(id='histogram_map', figure=fig_3)], color='#119DFF',
type='dot')
], width={'size': 12, 'offset': 0, 'order': 2}, style={"text-align": "left"}),
]),
])
]),
], xs=6),
dbc.Col([
dbc.Card([
dbc.CardBody([
dbc.Row([
dbc.Col([
html.H5('Overdue Status', style={"text-align": "center"}),
dcc.Loading(children=[dcc.Graph(id='overdue_map', figure={})], color='#119DFF', type='dot')
], width={'size': 12, 'offset': 0, 'order': 2}, style={"text-align": "left"}),
]),
])
]),
], xs=6),
], className='p-2 align-items-stretch')
])
#app.callback(
Output('overdue_map', 'figure'),
Input('histogram_map', 'clickData'))
def update_y_timeseries(clickData):
if clickData:
country_name = clickData['points'][0]['hovertext']
df_3 = df[df['BAS_DT'] == country_name]
df_4 = df_3.pivot_table(values='CLOC_CUR_XC_BL',
index=['APL_DTL_NAME'],
aggfunc=pd.Series.nunique).reset_index()
fig = px.bar(df_4,
x='APL_DTL_NAME',
y='CLOC_CUR_XC_BL'
, color_discrete_sequence=px.colors.qualitative.Alphabet)
fig.update_layout(xaxis_title="", yaxis_title="", plot_bgcolor='rgba(0,0,0,0)') # plot_bgcolor='rgba(0,0,0,0)'
fig.update_xaxes(showline=False, showgrid=False),
fig.update_yaxes(showline=False, showgrid=False, separatethousands=True)
fig.update_traces(width=0.3)
return fig
else:
raise PreventUpdate
if __name__ == "__main__":
app.run_server(debug=True)
I'm using clickData to return point as date, and then use this date to filter data and then make new bar graph.
Hope this help.
I have the written code below. I have two dropdown menus that work based on chained callbacks. the first dropdown menu gets the datasets and reads the columns' names and updates the options in the second dropdown menu. Then, the parameters can be plotted on the chart.
my dataframes look like this:
df={'col1':[12,15,25,33,26,33,39,17,28,25],
'col2':[35,33,37,36,36,26,31,21,15,29],
'col3':['A','A','A','A','B','B','B','B','B','B'],
'col4':[1,2,3,4,5,6,7,8,9,10]
I want to highlight the chart background depending on the categories in col3. I don't understand why when I select the dataset from the first dropdown menu the background color for col3 appears on the chart (before selecting the parameters). I have used Prevent_initial_call = True, but the second callback still triggers.
import dash
from dash import Dash, html, dcc, Output, Input, State, MATCH, ALL
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc
app = Dash(__name__)
app.layout = html.Div([
html.Div(children=[
html.Button('add Chart', id='add-chart', n_clicks=0)
]),
html.Div(id='container', children=[])
])
#app.callback(
Output('container', 'children'),
[Input('add-chart', 'n_clicks'),
Input({'type': 'remove-btn', 'index': ALL}, 'n_clicks')],
[State('container', 'children')],
prevent_initial_call=True
)
def display_graphs(n_clicks, n, div_children):
ctx = dash.callback_context
triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
elm_in_div = len(div_children)
if triggered_id == 'add-chart':
new_child = html.Div(
id={'type': 'div-num', 'index': elm_in_div},
style={'width': '25%',
'display': 'inline-block',
'outline': 'none',
'padding': 5},
children=[
dbc.Container([
dbc.Row([
dbc.Col([dcc.Dropdown(id={'type': 'dataset-choice', 'index': n_clicks},
options=['dataset1'],
clearable=True,
value=[]
)], width=6),
dbc.Col([dcc.Dropdown(id={'type': 'feature-choice', 'index': n_clicks},
options=[],
multi=True,
clearable=True,
value=[]
)], width=6)
]),
dbc.Row([
dbc.Col([dcc.Graph(id={'type': 'dynamic-graph','index': n_clicks},
figure={}
)])
]),
dbc.Row([
dbc.Col([html.Button("Remove", id={'type': 'remove-btn', 'index': elm_in_div})
])
]),
])
]
)
div_children.append(new_child)
return div_children
if triggered_id != 'add-chart':
for idx, val in enumerate(n):
if val is not None:
del div_children[idx]
return div_children
#app.callback(
Output({'type': 'feature-choice', 'index': MATCH}, 'options'),
[Input({'type': 'dataset-choice', 'index': MATCH}, 'value')],
prevent_initial_call=True
)
def set_dataset_options(chosen_dataset):
if chosen_dataset is None:
return dash.no_update
else:
path = 'C:/Users/pymnb/OneDrive/Desktop/test/'
df = pd.read_csv(path + chosen_dataset+'.csv')
features = df.columns.values[0:2]
return features
#app.callback(
Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
[Input({'type': 'dataset-choice', 'index': MATCH}, 'value'),
Input({'type': 'feature-choice', 'index': MATCH}, 'value')],
prevent_initial_call=True
)
def update_graph(chosen_dataset1, chosen_feature):
if chosen_feature is None:
return dash.no_update
if chosen_dataset1 is None:
return dash.no_update
path = 'C:/Users/pymnb/OneDrive/Desktop/test/'
df = pd.read_csv(path + chosen_dataset1+'.csv')
Xmin = df[chosen_feature].min().min()
print(Xmin)
Xmax = df[chosen_feature].max().max()
# to find the height of y-axis(col4)
col4_max = df['col4'].max()
col4_min = df['col4'].min()
fig1 = px.line(df, x=chosen_feature, y='col4')
fig1.update_layout({'height': 600,
'legend': {'title': '', 'x': 0, 'y': 1.06, 'orientation': 'h'},
'margin': {'l': 0, 'r': 20, 't': 50, 'b': 0},
'paper_bgcolor': 'black',
'plot_bgcolor': 'white',
}
)
fig1.update_yaxes(range=[col4_max, col4_min], showgrid=False)
fig1.update_xaxes(showgrid=False)
categ_col3 = df.col3.dropna().unique()
colors = ['#54FF9F', '#87CEFF']
for (i,j) in zip(categ_col3, colors):
index_min = df.loc[df.col3 == i].index[0]
index_max = df.loc[df.col3 == i].index[-1]
if index_min == 0:
cat_min = df['col4'][index_min]
else:
cat_min = df['col4'][index_min-1]
cat_max = df['col4'][index_max]
fig1.add_shape(type="rect", x0=Xmin, y0=cat_min, x1=Xmax, y1=cat_max,
fillcolor=j, layer='below', opacity=0.5,
)
return fig1
if __name__ == '__main__':
app.run_server(debug=True)
You can fix it by modifying your code to the following:
#app.callback(
Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
[Input({'type': 'dataset-choice', 'index': MATCH}, 'value'),
Input({'type': 'feature-choice', 'index': MATCH}, 'value')],
prevent_initial_call=True
)
def update_graph(chosen_dataset1, chosen_feature):
if (chosen_feature == []) or (chosen_dataset1 is None): #<--- correct the condition
return dash.no_update
else: #<---- add the else condition to prevent any update
Xmin = df[chosen_feature].min().min()
Xmax = df[chosen_feature].max().max()
The reason behind that because all the elements are created on fly and they are not within the app.layout. Please read the following from the documentation:
In other words, if the output of the callback is already present in
the app layout before its input is inserted into the layout,
prevent_initial_call will not prevent its execution when the input is
first inserted into the layout.
I’m trying to return name of points when clicking on Scatter Plot points but it just return one. How can I do to return multiple value when clicking multiple points. Below is my sample code:
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
PCA_table=pd.read_excel('https://github.com/hoatranobita/Jobs/blob/main/PCA_table.xlsx?raw=true')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(
options=PCA_table.columns.unique(),
value='PC1',
id='crossfilter-xaxis-column',
)],style={'width': '49%', 'display': 'inline-block'}),
html.Div([
dcc.Dropdown(
options=PCA_table.columns.unique(),
value='PC2',
id='crossfilter-yaxis-column'
)], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
], style={'padding': '10px 5px'}),
html.Div([
dcc.Graph(
id='crossfilter-indicator-scatter',
clickData={'points': [{'hovertext': '184A1'}]}
)], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
html.Div([
html.Div(id='x-time-series'),
], style={'display': 'inline-block', 'width': '49%'}),
])
#app.callback(
Output('crossfilter-indicator-scatter', 'figure'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-yaxis-column', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name):
fig = px.scatter(PCA_table,x=PCA_table[xaxis_column_name],
y=PCA_table[yaxis_column_name],
color=PCA_table['Labels'],
labels=PCA_table['Columns'],
hover_name=PCA_table['Columns'])
fig.update_layout(clickmode='event')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
return fig
#app.callback(
Output('x-time-series', 'children'),
Input('crossfilter-indicator-scatter', 'clickData'))
def update_y_timeseries(clickData):
click_name = clickData['points'][0]['hovertext']
return html.Span(click_name)
if __name__ == '__main__':
app.run_server(debug=False,port=1111)
As you see, I want to return click_name as multiple values not just one.
I’ve read about clickevent but still not get it. Thank you.
Use clickmode='event+select' to enable accumulation and to handle both click and selection events by listening for one single event (selectedData).
If layout.clickmode = 'event+select', selection data also accumulates
(or un-accumulates) selected data if you hold down the shift button
while clicking.
You can also set dragmode='select' if you want to modebar to be ready for selecting points (default mode is 'zoom').
fig.update_layout(
clickmode='event+select',
dragmode='select',
margin={'l': 40, 'b': 40, 't': 10, 'r': 0},
hovermode='closest'
)
Now in the callback, use selectedData instead of clickData :
#app.callback(
Output('x-time-series', 'children'),
Input('crossfilter-indicator-scatter', 'selectedData'),
prevent_initial_call=True)
def update_y_timeseries(selectedData):
selection= [p['hovertext'] for p in selectedData['points']]
return html.Span(', '.join(selection))
I'm trying to build a multi page Dash App with Long Callback . When I run the app with Single page Application , it is working fine .Working code is given Below
SinglePage.py
import json
import dash
from dash import html, dcc
from dash.long_callback import DiskcacheLongCallbackManager
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import datetime
import pandas as pd
import plotly.express as px
## Diskcache
import diskcache
from query_4_entity_explr import get_ORG_info, get_daterange_entity_dataframe
cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)
app = dash.Dash("topx_org",
long_callback_manager=long_callback_manager,
external_stylesheets=[dbc.themes.BOOTSTRAP],
meta_tags=[{'name': 'viewport', 'content': 'initial-scale=1'}])
today = datetime.datetime.today().date()
date_30_day_back = (datetime.datetime.today() - datetime.timedelta(days=1)).date()
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.H3("Explore Organizations mentioned in media", className='text-center text-primary py-5'), width=12)
]),
dbc.Row([
dbc.Col(dbc.Label(['Select daterange']), width={'size': 2, 'offset': 0}),
dbc.Col(dcc.DatePickerRange(id='input_date_picker_range', display_format='YYYY-MM-DD', start_date=date_30_day_back, end_date=today), width={'size':10, 'offset':0}),
], justify='around', className='mb-1'),
dbc.Row([
dbc.Col(dbc.Label(['Choose Companies with mention count']), width=2),
dbc.Col(dcc.Dropdown(id = "input_item_dropdown", options = [], value=[], multi=True), width=10),
], justify='around', className='mb-1'),
# dbc.Row(dbc.Button(id="cancel_button_id", children="Cancel Running Job!")),
dbc.Row([
dbc.Col(html.Div(id="output_text", children=[]), width={'size':10, 'offset':2}),
], justify='around', className='py-3'),
dbc.Row([
html.Progress(id="progress_bar"),
]),
dbc.Row([
dbc.Col(dcc.Graph(id='output_graph', figure={}), width=12),
], justify='around', className='py-5'),
dcc.Store(id='stored-dataframe')
])
#app.long_callback(
[Output(component_id='output_text', component_property='children'),
Output(component_id="input_item_dropdown", component_property="options"),
Output(component_id='stored-dataframe', component_property='data'),
],
[Input(component_id="input_date_picker_range", component_property="start_date"),
Input(component_id="input_date_picker_range", component_property="end_date"),
],
manager=long_callback_manager,
running=[
(Output("output_text", "disabled"), False, True),
(
Output("output_graph", "style"),
{"visibility": "hidden"},
{"visibility": "visible"},
),
(
Output("progress_bar", "style"),
{"visibility": "visible"},
{"visibility": "hidden"},
),
],
)
def update_figure(start_date, end_date):
print(start_date)
print(end_date)
# Data Acquisition
TOP_N = 20
unique_org_list, top_companys, org_gp_pdf = get_ORG_info(get_daterange_entity_dataframe(start_date,end_date), TOP_N)
# For the div
# _text = str(f"Between {start_date} to {end_date} for all the organizations : { ', '.join (item_list_value) }")
_text = str(f"Between {start_date} to {end_date} there are {len(unique_org_list)} organizations mentioned in media.")
# Store the data in browser storage.
datasets = {
'so_1': org_gp_pdf.to_json(orient='split', date_format='iso'),
'so_2': top_companys,
}
return [_text], [{'label': str(c[0]) + f" ({c[1]})", 'value': str(c[0])} for c in unique_org_list[0:]], json.dumps(datasets)
#app.callback(
[Output(component_id='output_graph', component_property='figure')],
[Input(component_id="input_item_dropdown", component_property="value"),
Input(component_id='stored-dataframe', component_property='data')
],
progress=[Output("progress_bar", "value"), Output("progress_bar", "max")],
)
def update_graph_2(item_list_value, jsonified_cleaned_data):
# Load the stored DF and List from Browser storage
datasets = json.loads(jsonified_cleaned_data)
df_2 = pd.read_json(datasets['so_1'], orient='split')
top_companys = datasets['so_2']
if len(item_list_value) == 0:
fig = px.bar( df_2[df_2.ORG.isin(top_companys)].sort_values(['NEWS_DATE'], ascending=True), title=f"Showing top {len(top_companys)} organizations", x="NEWS_DATE", y="COUNT", color="ORG", barmode="group") # .query("ORG=='Mentice' | ORG=='Isofol Medical AB'")
else:
queryString = " | ".join([f"ORG=='{item}'" for item in item_list_value])
fig = px.bar(df_2.query(queryString).sort_values(['NEWS_DATE'], ascending=True) ,title=f"Showing some selected organizations", x="NEWS_DATE", y="COUNT", color="ORG", barmode="group") # .query("ORG=='Mentice' | ORG=='Isofol Medical AB'")
# align title
fig.update_layout(title_x=0.5, xaxis=dict(title_text='NEWS PUBLISH DATE'), yaxis=dict(title_text='DAILY ORG MENTION COUNT '))
return [fig]
if _name_ == "__main__":
app.run_server(port=8050, debug=False)
But When i am trying run the same application in multipage structure it is showing me #long_callback is not callable.
In Multipage structure my main App file is given below
app.py
from pages import topx_org, topx_per
import dash
from dash import html, dcc
from dash.long_callback import DiskcacheLongCallbackManager
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
## Diskcache
import diskcache
cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)
app = dash.Dash(__name__,
suppress_callback_exceptions=True,
long_callback_manager=long_callback_manager,
external_stylesheets=[dbc.themes.BOOTSTRAP],
meta_tags=[{'name': 'viewport', 'content': 'initial-scale=1'}])
server = app.server
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
#app.callback(Output('page-content', 'children'),
Input('url', 'pathname'))
def display_page(pathname):
if pathname == '/topx_org':
return topx_org.layout
elif pathname == '/topx_per':
return topx_per.layout
else:
return '404'
if _name_ == '__main__':
app.run_server(debug=True)
and the topx_org.py is given below
from dash import callback, long_callback
import json
from dash import html, dcc
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import datetime
import pandas as pd
import plotly.express as px
from query_4_entity_explr import get_ORG_info, get_daterange_entity_dataframe
today = datetime.datetime.today().date()
date_30_day_back = (datetime.datetime.today() - datetime.timedelta(days=1)).date()
layout = dbc.Container([
dbc.Row([
dbc.Col(html.H3("Explore Organizations mentioned in media", className='text-center text-primary py-5'), width=12)
]),
dbc.Row([
dbc.Col(dbc.Label(['Select daterange' ]), width={'size':2, 'offset':0}),
dbc.Col(dcc.DatePickerRange(id='input_date_picker_range', display_format='YYYY-MM-DD',start_date=date_30_day_back, end_date=today), width={'size':10, 'offset':0}),
], justify='around', className='mb-1'),
dbc.Row([
dbc.Col(dbc.Label(['Choose Companies with mention count']), width=2),
dbc.Col(dcc.Dropdown(id = "input_item_dropdown", options = [], value=[], multi=True), width=10),
], justify='around', className='mb-1'),
dbc.Row([
dbc.Col(html.Div(id="output_text", children=[]), width={'size':10, 'offset':2}),
], justify='around', className='py-3'),
dbc.Row([
dbc.Col(dcc.Graph(id='output_graph', figure={}), width=12),
], justify='around', className='py-5'),
dcc.Store(id='stored-dataframe')
])
# Output(component_id='output_graph', component_property='figure'),
#callback(
[Output(component_id='output_text', component_property='children'),
Output(component_id="input_item_dropdown", component_property="options"),
Output(component_id='stored-dataframe', component_property='data'),
],
[Input(component_id="input_date_picker_range", component_property="start_date"),
Input(component_id="input_date_picker_range", component_property="end_date"),
],
progress=[Output("progress_bar", "value"), Output("progress_bar", "max")],
)
def update_figure(start_date, end_date):
print(start_date)
print(end_date)
# print(item_list_value)
# Data Acquisition
TOP_N = 20
unique_org_list, top_companys, org_gp_pdf = get_ORG_info(get_daterange_entity_dataframe(start_date,end_date), TOP_N)
# For the div
# _text = str(f"Between {start_date} to {end_date} for all the organizations : { ', '.join (item_list_value) }")
_text = str(f"Between {start_date} to {end_date} there are {len(unique_org_list)} organizations mentioned in media.")
# Store the data in browser storage.
datasets = {
'so_1': org_gp_pdf.to_json(orient='split', date_format='iso'),
'so_2': top_companys,
}
return [_text], [{'label': str(c[0]) + f" ({c[1]})", 'value': str(c[0])} for c in unique_org_list[0:]], json.dumps(datasets)
#long_callback(
[Output(component_id='output_graph', component_property='figure')],
[Input(component_id="input_item_dropdown", component_property="value"),
Input(component_id='stored-dataframe', component_property='data')
],
running=[
(Output("output_text", "disabled"), False, True),
(
Output("output_graph", "style"),
{"visibility": "hidden"},
{"visibility": "visible"},
),
(
Output("progress_bar", "style"),
{"visibility": "visible"},
{"visibility": "hidden"},
),
],
)
def update_graph_2(item_list_value, jsonified_cleaned_data):
# Load the stored DF and List from Browser storage
datasets = json.loads(jsonified_cleaned_data)
df_2 = pd.read_json(datasets['so_1'], orient='split')
top_companys = datasets['so_2']
if len(item_list_value) == 0:
fig = px.bar( df_2[df_2.ORG.isin(top_companys)].sort_values(['NEWS_DATE'], ascending=True), title=f"Showing top {len(top_companys)} organizations", x="NEWS_DATE", y="COUNT", color="ORG", barmode="group") # .query("ORG=='Mentice' | ORG=='Isofol Medical AB'")
else:
queryString = " | ".join([f"ORG=='{item}'" for item in item_list_value])
fig = px.bar(df_2.query(queryString).sort_values(['NEWS_DATE'], ascending=True) ,title=f"Showing some selected organizations", x="NEWS_DATE", y="COUNT", color="ORG", barmode="group") # .query("ORG=='Mentice' | ORG=='Isofol Medical AB'")
# align title
fig.update_layout(title_x=0.5, xaxis=dict(title_text='NEWS PUBLISH DATE'), yaxis=dict(title_text='DAILY ORG MENTION COUNT '))
return [fig]
I am having the following error
#long_callback(
TypeError: 'module' object is not callable
(Updated) According to this post, this is not possible yet because long_callback requires the app object (#app.long_callback). If you try to import the app object in your page file, you will create a circular import which will cause an error.
The only possibility mentionned in the post is to put all the long callbacks in the app.py file. The 'normal' callbacks and the layout can stay in the pages files.
This is now possible in the latest dash version by using the function app = dash.get_app() and then calling #app.long_callback.