So, I’m dynamically adding dcc.graphs
to a div
at the click of a button using pattern matching callbacks.
so when I click the button highlighted in red, the entire blue portion gets added below it.
I am also using another callback like this
@app.callback(Output({'type':'graph-1','index':MATCH}, 'extendData'),
Input({'type':'interval','index':MATCH}, 'n_intervals'))
def update_graph(graph):
/*some logic*/
return data /*[ideally should be on len 1 as it gets updated every 1 second]*/
What this callback does is update the graph every 1 second with real-time values when the sync toggle is turned on.
This works fine and does the job that I want it to do. However, when I add/delete another blue plot to the main div
component, the update_graph
function gets messed up and gives this kind of output. (Based on correct values it should have been a straight line…similar to 0-8 on x axis).
PS. I can add/delete as many graphs as I want before starting the sync toggle on any graph. But, if I do it during/after the sync toggle then I get this kind of a result.
Also adding a few callbacks for more clarification
def new_time_sig_plot(idx):
new_div = html.Div(id={'type':'t-plot','index':idx}, children=[
html.Div(id={'type': 'time-sig-options','index':idx}, className='filters', children=[
daq.ToggleSwitch(id={'type':'sync-toggle','index':idx}, label='SYNC', value=False, size=40, color='#2D6BCF'),
dcc.Dropdown( placeholder='group', id={'type': 'group-filter','index':idx}, className='input-attrs', options=group_options, value=None),
dcc.Dropdown( placeholder='variable', id={'type': 'variable-filter','index':idx}, className='input-attrs', options=variable_options, value=None),
dcc.Dropdown( placeholder='node', id={'type': 'node-filter','index': idx}, className='input-attrs',
options=[dict(label=option,value=option) for option in nodes_options], value=nodes_options[-1]),
dcc.Dropdown( placeholder='component', id={'type': 'component-filter','index':idx}, className='input-attrs', options=component_options, value=None),
html.Button('⚙ Filter', id={'type': 'apply-time-filters', 'index':idx},className="filter-btn", n_clicks=0),
html.Button('🗑️', id={'type': 'del-t-plt', 'index':idx},className='del'),
]),
dcc.Dropdown(placeholder='Choose time signal',id={'type': 't-signal-selector', 'index':idx},options=all_time_signals,multi=True,value=[]),
dcc.Graph(id={'type':'graph-1','index':idx}, figure=dict(data=[],layout=time_sig_layout), animate=True),
dcc.Interval(id={'type':'interval','index':idx},interval=1*1000)
])
return new_div
def new_node_sig_div(idx):
#pretty similar to the above function but it doesn't use the update_graph callback so I'm skipping it
return new_div
@app.callback(
[Output('time-sig-div', 'children'), Output('node-sig-div', 'children')],
[Input('new-t-plt', 'n_clicks'), Input({'type': 'del-t-plt','index': ALL}, 'n_clicks'),
Input('new-n-plt', 'n_clicks'), Input({'type': 'del-n-plt','index': ALL}, 'n_clicks')],
[State('time-sig-div', 'children'), State('node-sig-div', 'children')],
prevent_initial_call = False)
def edit_time_div(add_time, del_time, add_node, del_node, time_div, node_div):
global group_options; global variable_options; global component_options; global all_time_signals; global all_node_signals;
triggered = [t["prop_id"] for t in dash.callback_context.triggered][0]
add_time = add_time or 0; add_node = add_node or 0
if triggered == 'new-t-plt.n_clicks' or triggered == '.':
if len(time_div) == 5: return dash.no_update
time_div.append(new_time_sig_plot(add_time))
if triggered == 'new-n-plt.n_clicks' or triggered == '.':
if len(node_div) == 5: return dash.no_update
node_div.append(new_node_sig_div(add_node))
if triggered[-20:-17] == 'del':
ID = json.loads(triggered[0:-9])
del_idx = ID["index"]
if ID["type"] == 'del-t-plt':
for div_idx, div in enumerate(time_div):
if del_idx == div["props"]["id"]["index"]:
del time_div[div_idx]
break
else:
for div_idx, div in enumerate(node_div):
if del_idx == div["props"]["id"]["index"]:
del node_div[div_idx]
break
# print(time_div[0])
return time_div, node_div