J'ai rencontré un comportement étrange - je vois des questions similaires sur les forums de Plotly et sur Stackoverflow, mais pas de solution. En gros, j'essaie de stocker une valeur intermédiaire (à réutiliser dans d'autres callbacks) dans une div cachée 'data-storage-json', mais le callback qui l'a comme Input ne semble pas avoir lieu. Il n'y a pas d'erreur sur le backend. Sur le front-end, j'obtiens "Callback error updating plot-div.children" (qui est le composant spécifié comme Output).
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.exceptions import PreventUpdate
########### Layout:
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
html.Div(id='data-storage-json', style={'display': 'none'}),
html.Div(children=[
dash_table.DataTable(
id='event-table',
style_data={'whiteSpace': 'normal'}, #'border': '1px solid blue'},
style_cell={'textAlign': 'center'},
#style_header={ 'border': '1px solid pink' },
css=[{
'selector': '.dash-cell div.dash-cell-value',
'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
}],
columns=[{"name": i, "id": i} for i in event_df.columns if i is not 'id'],
style_table={'overflowX': 'scroll'},
row_selectable='single',
selected_rows=[],
page_current=0,
page_size=PAGE_SIZE,
page_action='custom',
filter_action='custom',
filter_query='',
sort_action='custom',
sort_mode='multi',
sort_by=[]
),
html.Div(id='event-stats', style={'width': '80%', 'color': 'black', 'font-size': '9'})],
style={'width': '90%', 'margin-left': '20px', 'font-size': '9', 'horizontal-align': 'middle', 'vertical-align': 'middle'}),
html.Div(children=[html.Br()]),
html.Button('Plot', id='show-button'),
html.Div(id='plot-div', children=[], style={'width': '95%', 'font-size': '9', 'vertical-align': 'middle'}),
])
########### Callbacks:
'''
Callback for sorting/filtering table
'''
@app.callback(
[Output('event-table', 'data'),
Output('event-table', 'page_count'),
Output('event-stats', 'children')],
[Input('event-table', 'sort_by'),
Input('event-table', 'filter_query'),
Input('event-table', 'page_current'),
Input('event-table', 'page_size')])
def update_event_selection(sort_by, filter_query,page_current, page_size):
dff = sort_filter_table(event_df, filter_query, sort_by)
res = dff.iloc[page_current*page_size: (page_current + 1)*page_size]
page_count = int(dff.shape[0]/page_size)+1
stat_str = '{} events in the table. Displaying page {} of {}'.format(dff.shape[0], page_current+1, page_count)
return res.to_dict('records'), page_count, stat_str
@app.callback(
Output('data-storage-json','children'),
[Input('show-button', 'n_clicks')],
[State('event-table','selected_row_ids')
])
def prepare_data(n_clicks,selected_id):
duration=1
print('Selected id: ',selected_id)
if n_clicks is None or selected_id is None or len(selected_id)==0:
raise PreventUpdate
duration=int(duration)
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
print(row)
event_time=pd.to_datetime(row['Start'],errors='ignore')
# sensors to load:
flist=['ip_m','vp_m','f','df']
print('Duration {}'.format(duration))
res_df=get_event_data(interconnect,event_time,duration, feature_list=flist)
print(res_df.shape)
js=res_df.to_json(date_format='iso', orient='split')
print('In Prep: ',len(js))
return js
@app.callback(
Output('plot-div','children'),
[Input('data-storage-json','children')],
[State('event-table','selected_row_ids')])
def generate_plots(data_storage,selected_id):
if data_storage is None:
print('None!!!')
raise PreventUpdate
else:
print('InDisplay -storage: '+str(len(data_storage)))
res_df = pd.read_json(data_storage, orient='split')
print('InDisplay ',res_df.shape)
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
event_time=pd.to_datetime(row['Start'],errors='ignore')
event_type=row['Event']+': '+row['Cause']
event_pid=''
# columns sorted in reverse alphabetical
flist=sorted(np.unique([c.split('__')[1] for c in res_df.columns]))[::-1]
print('To plot: ',res_df.shape)
# generate plots for each type of sensor:
fig_list=[]
for feature in flist:
col_list = [c for c in res_df.columns if not c.startswith('_') and c.endswith('_'+feature)]
temp_df = res_df[col_list]
# plot results
print('Preparing figure '+feature)
fig=temp_df.iplot(kind='scatter',mode='markers',size=3, title="Plot {}: {} {} {}".format(feature,event_time,event_type,event_pid), asFigure=True)
#fig_list.append(fig)
fig_list.append((html.Div(children=[dcc.Graph(id=feature+'-scatter',figure=fig)])))
print('Figure done')
return fig_list
########### Run the app:
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--gpu', type=int, default=0, help='number of GPU to use for calculations')
parser.add_argument('--port', type=int, default=8050, help='port on which to run (default: 8050)')
options,_ = parser.parse_known_args()
os.environ['CUDA_VISIBLE_DEVICES'] = str(options.gpu)
app.run_server(debug=True, port = options.port)
UPD : event_df est quelque chose comme ça :
event_df = pd.DataFrame({"id": [0,1,2],
"Start": ["2016-01-01 14:33","2016-01-01 16:45","2016-01-01 17:46"],
"Event": ["Line Outage","Line Outage","Line Outage"],
})
J'ai également inclus un exemple de code autonome dans une réponse ci-dessous
Versions du paquet :
dash 1.8.0 py_0 conda-forge
dash-core-components 1.7.0 py_0 conda-forge
dash-html-components 1.0.2 py_0 conda-forge
dash-renderer 1.2.3 py_0 conda-forge
dash-table 4.6.0 py_0 conda-forge
UPDATE : En fin de compte, le problème semble être dû à la taille de la base de données. Hidden-div ou Store ne pouvait gérer que quelques centaines de lignes. J'ai donc décidé d'utiliser Flask Caching/Memoization : voir https://dash.plotly.com/sharing-data-between-callbacks o https://dash.plotly.com/performance