Good afternoon everyone! 🙂
What is the problem. I have 4 plots and the challenge is to keep them scaling in sync (i.e. if you zoom in on 1 plot, then the rest of the plots are zoomed in as well, etc.).
Now all I have done is that I can zoom in on one particular graph (‘graph1’) and the rest are zoomed in with it. This is what it looks like.
@app.callback( Output('graph1', 'figure'), Output('graph2', 'figure'), Output('graph3', 'figure'),Output('graph4', 'figure'), Input('graph1', 'relayoutData'), State('graph1', 'figure'), State('graph2', 'figure'), State('graph3', 'figure'), State('graph4', 'figure') ) def zoom_event(relayout_data, *figures): outputs = [] for fig in figures: try: fig['layout']["xaxis"]["range"] = [relayout_data['xaxis.range[0]'], relayout_data['xaxis.range[1]']] fig['layout']["xaxis"]["autorange"] = False except (KeyError, TypeError) as e: fig['layout']["xaxis"]["autorange"] = True outputs.append(fig) return outputs
If you try to do something that looks like synchronizing charts, you will get countless errors. I tried to make 2 figures with subplots, and even one figure with 4 subplots, but all in vain.
One of the latter was that outputs accept more inputs than there are outputs. But I haven’t figured it out specifically in this error. I tried to do so.
@app.callback( Output('graph1', 'figure'), Output('graph2', 'figure'), Output('graph3', 'figure'), Output('graph4', 'figure'), Input('graph1', 'relayoutData'), Input('graph2', 'relayoutData'), Input('graph3', 'relayoutData'), Input('graph4', 'relayoutData'), State('graph1', 'figure'), State('graph2', 'figure'), State('graph3', 'figure'), State('graph4', 'figure') )
Thank you if you dare to help.
For searches, I recommend going here Dygraphs – synchronous zooming
Here the participant solved a rather similar problem (but it is written in javascript, I will be glad to hear suggestions on how this can be combined)
And also here How to get zoom level in time series data as callback input in Dash
Advertisement
Answer
I fought with this for a while and this was the solution I eventually came up with.
In my use case I’m dynamically adding graphs by
dcc.Graph({'type': 'graph', 'index': index_counter})
Linking the zoom behavior
@app.callback( Output({'type': 'graph', 'index': ALL}, 'relayoutData'), Output({'type': 'graph', 'index': ALL}, 'figure'), Input({'type': 'graph', 'index': ALL}, 'relayoutData'), State({'type': 'graph', 'index': ALL}, 'figure')) def LinkedZoom(relayout_data, figure_states): unique_data = None for data in relayout_data: if relayout_data.count(data) == 1: unique_data = data if unique_data: for figure_state in figure_states: if unique_data.get('xaxis.autorange'): figure_state['layout']['xaxis']['autorange'] = True figure_state['layout']['yaxis']['autorange'] = True else: figure_state['layout']['xaxis']['range'] = [ unique_data['xaxis.range[0]'], unique_data['xaxis.range[1]']] figure_state['layout']['xaxis']['autorange'] = False figure_state['layout']['yaxis']['range'] = [ unique_data['yaxis.range[0]'], unique_data['yaxis.range[1]']] figure_state['layout']['yaxis']['autorange'] = False return [unique_data] * len(relayout_data), figure_states return relayout_data, figure_states
Reference commit: https://github.com/djhedges/exit_speed/commit/863efbf00250c79663dd460f6ab2bd54e051b9d7