88
99__author__ = "Jonas Van Der Donckt, Jeroen Van Der Donckt, Emiel Deprost"
1010
11+ import itertools
1112import re
1213from abc import ABC
1314from collections import namedtuple
@@ -393,12 +394,37 @@ def _nest_dict_rec(k: str, v: any, out: dict) -> None:
393394
394395 return trace
395396
397+ def _layout_xaxis_to_trace_xaxis_mapping (self ) -> Dict [str , List [str ]]:
398+ """Construct a dict which maps the layout xaxis keys to the trace xaxis keys.
399+
400+ Returns
401+ -------
402+ Dict[str, List[str]]
403+ A dict with the layout xaxis values as keys and the trace its corresponding
404+ xaxis anchor value.
405+
406+ """
407+ # edge case: an empty `go.Figure()` does not yet contain axes keys
408+ if self ._grid_ref is None :
409+ return {"xaxis" : ["x" ]}
410+
411+ mapping_dict = {}
412+ for sub_plot in itertools .chain .from_iterable (self ._grid_ref ): # flattten
413+ sub_plot = [] if sub_plot is None else sub_plot
414+ for axes in sub_plot : # NOTE: you can have multiple axes in a subplot
415+ layout_xaxes = axes .layout_keys [0 ]
416+ trace_xaxes = axes .trace_kwargs ["xaxis" ]
417+
418+ # append the trace xaxis to the layout xaxis key its value list
419+ mapping_dict .setdefault (layout_xaxes , []).append (trace_xaxes )
420+ return mapping_dict
421+
396422 def _check_update_figure_dict (
397423 self ,
398424 figure : dict ,
399425 start : Optional [Union [float , str ]] = None ,
400426 stop : Optional [Union [float , str ]] = None ,
401- xaxis_filter : str = None ,
427+ layout_xaxis_filter : Optional [ str ] = None ,
402428 updated_trace_indices : Optional [List [int ]] = None ,
403429 ) -> List [int ]:
404430 """Check and update the traces within the figure dict.
@@ -421,8 +447,9 @@ def _check_update_figure_dict(
421447 The start time for the new resampled data view, by default None.
422448 stop : Union[float, str], optional
423449 The end time for the new resampled data view, by default None.
424- xaxis_filter: str, optional
425- Additional trace-update subplot filter, by default None.
450+ layout_xaxis_filter: str, optional
451+ Additional layout xaxis filter, e.g. the affected x-axis values by the
452+ triggered relayout event (e.g. xaxis), by default None.
426453 updated_trace_indices: List[int], optional
427454 List of trace indices that already have been updated, by default None.
428455
@@ -433,71 +460,23 @@ def _check_update_figure_dict(
433460 modalities which are updated.
434461
435462 """
436- xaxis_filter_short = None
437- if xaxis_filter is not None :
438- xaxis_filter_short = "x" + xaxis_filter .lstrip ("xaxis" )
439-
440463 if updated_trace_indices is None :
441464 updated_trace_indices = []
442465
466+ if layout_xaxis_filter is not None :
467+ layout_trace_mapping = self ._layout_xaxis_to_trace_xaxis_mapping ()
468+ # Retrieve the trace xaxis values that are affected by the relayout event
469+ trace_xaxis_filter : List [str ] = layout_trace_mapping [layout_xaxis_filter ]
470+
443471 for idx , trace in enumerate (figure ["data" ]):
444- # We skip when the trace-idx already has been updated.
445- if idx in updated_trace_indices :
472+ # We skip when (i) the trace-idx already has been updated or (ii) when
473+ # there is a layout_xaxis_filter and the trace xaxis is not in the filter
474+ if idx in updated_trace_indices or (
475+ layout_xaxis_filter is not None
476+ and trace .get ("xaxis" , "x" ) not in trace_xaxis_filter
477+ ):
446478 continue
447479
448- if xaxis_filter is not None :
449- # the x-anchor of the trace is stored in the layout data
450- if trace .get ("yaxis" ) is None :
451- # TODO In versions up until v0.8.2 we made the assumption that yaxis
452- # = xaxis_filter_short. -> Why did we make this assumption?
453- y_axis = "y" # + xaxis_filter[1:]
454- else :
455- y_axis = "yaxis" + trace .get ("yaxis" )[1 :]
456-
457- # Also check for overlaying traces - fixes #242
458- overlaying = figure ["layout" ].get (y_axis , {}).get ("overlaying" )
459- if overlaying :
460- y_axis = "yaxis" + overlaying [1 :]
461-
462- # Next to the x-anchor, we also fetch the xaxis which matches the
463- # current trace (i.e. if this value is not None, the axis shares the
464- # x-axis with one or more traces).
465- # This is relevant when e.g. fig.update_traces(xaxis='x...') was called.
466- x_anchor_trace = figure ["layout" ].get (y_axis , {}).get ("anchor" )
467- if x_anchor_trace is not None :
468- xaxis_matches = (
469- figure ["layout" ]
470- .get ("xaxis" + x_anchor_trace .lstrip ("x" ), {})
471- .get ("matches" )
472- )
473- else :
474- xaxis_matches = figure ["layout" ].get ("xaxis" , {}).get ("matches" )
475-
476- # print(
477- # f"x_anchor: {x_anchor_trace} - xaxis_filter: {xaxis_filter} ",
478- # f"- xaxis_matches: {xaxis_matches}"
479- # )
480-
481- # We skip when:
482- # * the change was made on the first row and the trace its anchor is not
483- # in [None, 'x'] and the matching (a.k.a. shared) xaxis is not equal
484- # to the xaxis filter argument.
485- # -> why None: traces without row/col argument and stand on first row
486- # and do not have the anchor property (hence the DICT.get() method)
487- # * x_axis_filter_short not in [x_anchor or xaxis matches] for
488- # NON first rows
489- if (
490- xaxis_filter_short == "x"
491- and (
492- x_anchor_trace not in (None , "x" )
493- and xaxis_matches != xaxis_filter_short
494- )
495- ) or (
496- xaxis_filter_short != "x"
497- and (xaxis_filter_short not in (x_anchor_trace , xaxis_matches ))
498- ):
499- continue
500-
501480 # If we managed to find and update the trace, it will return the trace
502481 # and thus not None.
503482 updated_trace = self ._check_update_trace_data (trace , start = start , end = stop )
@@ -1361,7 +1340,7 @@ def construct_update_data(
13611340 current_graph ,
13621341 start = relayout_data [t_start_key ],
13631342 stop = relayout_data [t_stop_key ],
1364- xaxis_filter = xaxis ,
1343+ layout_xaxis_filter = xaxis ,
13651344 updated_trace_indices = updated_trace_indices ,
13661345 )
13671346
@@ -1377,7 +1356,7 @@ def construct_update_data(
13771356 xaxis = autorange_key .split ("." )[0 ]
13781357 updated_trace_indices = self ._check_update_figure_dict (
13791358 current_graph ,
1380- xaxis_filter = xaxis ,
1359+ layout_xaxis_filter = xaxis ,
13811360 updated_trace_indices = updated_trace_indices ,
13821361 )
13831362 # 2.1. Autorange -> do nothing, the autorange will be applied on the
0 commit comments