Hi again David , sorry to bug you once more with this.
Adding the line to generate the graphs id really helped, thank you.
I got the app to mostly work, but something strange is happening and no matter how much I tracked the callback progress and outputs/inputs through printing and breakpoints, I really don’t understand what is happening.
If I start the app, then select 3 from the dropdown (thereby generating 3 graphs), zooming in and out on any of the 3 graphs triggers the same zoom on all other graphs (although if I zoom in one graph, and then double click on a different graph from the one I zoomed in, the reset doesn’t show the whole y axis on the initial graph used for the zoom).
If I select 2 from the dropdown (thereby generating 2 graphs), zooming on the first graph doesn’t apply the zoom to the second graph, but zooming on the second graph does apply the zoom to the first graph.
Please see below the latest version of the reproductible example, with lots of prints to keep track of the data throughout the callbacks as well as a counter for how many times the callback is fired.
import dash
import plotly.graph_objs as go
import pandas as pd
from dash import Input, Output, ALL, dcc, html, State
import datetime as dt
app = dash.Dash(__name__)
# Load data
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
df.columns = [col.replace("AAPL.", "") for col in df.columns]
df['Date'] = pd.to_datetime(df['Date'])
dropdown_options = [{'label': cat, 'value': cat} for cat in [1,2,3]]
app.layout = html.Div([
dcc.Input(id='number-input', type='number', value=0),
dcc.Dropdown(id='graphsnumber-dropdown', options=dropdown_options, value=1),
html.Div(id='graph-container') ,
html.Div(id='OutputContainer')
])
callback_counter = 0
@app.callback(
Output('graph-container', 'children'),
[Input('graphsnumber-dropdown', 'value')])
def generate_graphs(graphs_num):
graphs = []
print(graphs_num)
for each_num in range(0, graphs_num):
temp_graph = go.Figure()
temp_graph.add_trace(
go.Scatter(x=list(df.Date), y=list(df.High)))
graphs.append(dcc.Graph(figure=temp_graph, id={'type': 'graph', 'index': each_num}))
return graphs
@app.callback(
Output({'type': 'graph', 'index': ALL}, 'relayoutData'),
Output({'type': 'graph', 'index': ALL}, 'figure'),
Output('number-input', 'value'),
Input('number-input', 'value'),
Input({'type': 'graph', 'index': ALL}, 'relayoutData'),
State({'type': 'graph', 'index': ALL}, 'figure'))
def LinkedZoom(callback_counter, relayout_data, figure_states):
callback_counter = callback_counter + 1
print('Firing callback number : ' + str(callback_counter))
print(' ')
print('*********************************************************************************')
print('Number of figures at start of callback: ' + str(len(figure_states)) + '.')
print(' ')
i = 1
for each_f in figure_states:
for layout_k in [k for k in each_f['layout'].keys()]:
if layout_k == 'xaxis':
print('Figure ' + str(i) + ' xaxis layout state looks like this: ')
print(each_f['layout']['xaxis'])
print(' ')
if layout_k == 'autosize':
print('Figure ' + str(i) + ' autosize is set to: ')
print(each_f['layout']['autosize'])
print(' ')
i = i + 1
print(' ')
print('*********************************************************************************')
print('Relayout data at start of callback: ')
print(relayout_data)
unique_data = None
for data in relayout_data:
if relayout_data.count(data) == 1:
print(' ')
print('*********************************************************************************')
print('data in ralayout_data when there is only 1 figure : ')
print(data)
unique_data = data
else:
print(' ')
print('*********************************************************************************')
print('data in ralayout_data when length of relayout_data is : ' + str(len(relayout_data)))
print(data)
if unique_data:
for figure_state in figure_states:
print(' ')
print('*********************************************************************************')
if unique_data.get('xaxis.autorange'):
print(' ')
print('If the layout in unique_data contains xaxis.autorange, xaxis reset on all figures.')
figure_state['layout']['xaxis']['autorange'] = True
else:
if 'xaxis.range[0]' in unique_data.keys():
print(' ')
print('Given the user zoom, unique_data xaixs.range is :')
print([unique_data['xaxis.range[0]'], unique_data['xaxis.range[1]']])
figure_state['layout']['xaxis']['range'] = [unique_data['xaxis.range[0]'], unique_data['xaxis.range[1]']]
figure_state['layout']['xaxis']['autorange'] = False
else:
print(' ')
print('unqiue_data does not contain an xaxis.range[0]')
else:
print(' ')
print('no zoom made on any figure.')
return [unique_data] * len(relayout_data), figure_states, callback_counter
else:
print('*********************************************************************************')
print('Unique_data is None')
return relayout_data, figure_states, callback_counter
if __name__ == '__main__':
app.run_server(debug=False)
Please let me know if you see where things go wrong.
That solution is SO close .
Thank you.