1111__author__ = "Jonas Van Der Donckt, Jeroen Van Der Donckt, Emiel Deprost"
1212
1313import warnings
14- from typing import Tuple
14+ from typing import Tuple , List
1515
1616import dash
1717import plotly .graph_objects as go
@@ -41,26 +41,98 @@ def __init__(
4141 show_mean_aggregation_size : bool = True ,
4242 convert_traces_kwargs : dict | None = None ,
4343 verbose : bool = False ,
44+ show_dash_kwargs : dict | None = None ,
4445 ):
46+ """Initialize a dynamic aggregation data mirror using a dash web app.
47+
48+ Parameters
49+ ----------
50+ figure: BaseFigure
51+ The figure that will be decorated. Can be either an empty figure
52+ (e.g., ``go.Figure()``, ``make_subplots()``, ``go.FigureWidget``) or an
53+ existing figure.
54+ convert_existing_traces: bool
55+ A bool indicating whether the high-frequency traces of the passed ``figure``
56+ should be resampled, by default True. Hence, when set to False, the
57+ high-frequency traces of the passed ``figure`` will not be resampled.
58+ default_n_shown_samples: int, optional
59+ The default number of samples that will be shown for each trace,
60+ by default 1000.\n
61+ .. note::
62+ * This can be overridden within the :func:`add_trace` method.
63+ * If a trace withholds fewer datapoints than this parameter,
64+ the data will *not* be aggregated.
65+ default_downsampler: AbstractSeriesDownsampler
66+ An instance which implements the AbstractSeriesDownsampler interface and
67+ will be used as default downsampler, by default ``EfficientLTTB`` with
68+ _interleave_gaps_ set to True. \n
69+ .. note:: This can be overridden within the :func:`add_trace` method.
70+ resampled_trace_prefix_suffix: str, optional
71+ A tuple which contains the ``prefix`` and ``suffix``, respectively, which
72+ will be added to the trace its legend-name when a resampled version of the
73+ trace is shown. By default a bold, orange ``[R]`` is shown as prefix
74+ (no suffix is shown).
75+ show_mean_aggregation_size: bool, optional
76+ Whether the mean aggregation bin size will be added as a suffix to the trace
77+ its legend-name, by default True.
78+ convert_traces_kwargs: dict, optional
79+ A dict of kwargs that will be passed to the :func:`add_traces` method and
80+ will be used to convert the existing traces. \n
81+ .. note::
82+ This argument is only used when the passed ``figure`` contains data and
83+ ``convert_existing_traces`` is set to True.
84+ verbose: bool, optional
85+ Whether some verbose messages will be printed or not, by default False.
86+ show_dash_kwargs: dict, optional
87+ A dict that will be used as default kwargs for the :func:`show_dash` method.
88+ Note that the passed kwargs will be take precedence over these defaults.
89+
90+ """
4591 # Parse the figure input before calling `super`
46- if is_figure (figure ) and not is_fr (figure ): # go.Figure
47- # Base case, the figure does not need to be adjusted
92+ if is_figure (figure ) and not is_fr (figure ):
93+ # A go.Figure
94+ # => base case: the figure does not need to be adjusted
4895 f = figure
4996 else :
5097 # Create a new figure object and make sure that the trace uid will not get
5198 # adjusted when they are added.
5299 f = self ._get_figure_class (go .Figure )()
53100 f ._data_validator .set_uid = False
54101
55- if isinstance (figure , BaseFigure ): # go.FigureWidget or AbstractFigureAggregator
56- # A base figure object, we first copy the layout and grid ref
102+ if isinstance (figure , BaseFigure ):
103+ # A base figure object, can be;
104+ # - a go.FigureWidget
105+ # - a plotly-resampler figure: subclass of AbstractFigureAggregator
106+ # => we first copy the layout, grid_str and grid ref
57107 f .layout = figure .layout
108+ f ._grid_str = figure ._grid_str
58109 f ._grid_ref = figure ._grid_ref
59110 f .add_traces (figure .data )
111+ elif isinstance (figure , dict ) and (
112+ "data" in figure or "layout" in figure # or "frames" in figure # TODO
113+ ):
114+ # A figure as a dict, can be;
115+ # - a plotly figure as a dict (after calling `fig.to_dict()`)
116+ # - a pickled (plotly-resampler) figure (after loading a pickled figure)
117+ # => we first copy the layout, grid_str and grid ref
118+ f .layout = figure .get ("layout" )
119+ f ._grid_str = figure .get ("_grid_str" )
120+ f ._grid_ref = figure .get ("_grid_ref" )
121+ f .add_traces (figure .get ("data" ))
122+ # `pr_props` is not None when loading a pickled plotly-resampler figure
123+ f ._pr_props = figure .get ("pr_props" )
124+ # `f._pr_props`` is an attribute to store properties of a
125+ # plotly-resampler figure. This attribute is only used to pass
126+ # information to the super() constructor. Once the super constructor is
127+ # called, the attribute is removed.
128+
129+ # f.add_frames(figure.get("frames")) TODO
60130 elif isinstance (figure , (dict , list )):
61131 # A single trace dict or a list of traces
62132 f .add_traces (figure )
63133
134+ self ._show_dash_kwargs = show_dash_kwargs if show_dash_kwargs is not None else {}
135+
64136 super ().__init__ (
65137 f ,
66138 convert_existing_traces ,
@@ -129,7 +201,9 @@ def show_dash(
129201 ``config`` parameter for this property in this method.
130202 See more https://dash.plotly.com/dash-core-components/graph
131203 **kwargs: dict
132- Additional app.run_server() kwargs. e.g.: port
204+ Additional app.run_server() kwargs. e.g.: port, ...
205+ Also note that these kwargs take precedence over the ones passed to the
206+ constructor via the ``show_dash_kwargs`` argument.
133207
134208 """
135209 graph_properties = {} if graph_properties is None else graph_properties
@@ -150,14 +224,19 @@ def show_dash(
150224
151225 # 2. Run the app
152226 if (
153- self .layout .height is not None
154- and mode == "inline"
227+ mode == "inline"
155228 and "height" not in kwargs
156229 ):
157- # If figure height is specified -> re-use is for inline dash app height
158- kwargs ["height" ] = self .layout .height + 18
230+ # If app height is not specified -> re-use figure height for inline dash app
231+ # Note: default layout height is 450 (whereas default app height is 650)
232+ # See: https://plotly.com/python/reference/layout/#layout-height
233+ fig_height = self .layout .height if self .layout .height is not None else 450
234+ kwargs ["height" ] = fig_height + 18
235+
236+ # kwargs take precedence over the show_dash_kwargs
237+ kwargs = {** self ._show_dash_kwargs , ** kwargs }
159238
160- # store the app information, so it can be killed
239+ # Store the app information, so it can be killed
161240 self ._app = app
162241 self ._host = kwargs .get ("host" , "127.0.0.1" )
163242 self ._port = kwargs .get ("port" , "8050" )
@@ -213,3 +292,11 @@ def register_update_graph_callback(
213292 dash .dependencies .Input (graph_id , "relayoutData" ),
214293 prevent_initial_call = True ,
215294 )(self .construct_update_data )
295+
296+ def _get_pr_props_keys (self ) -> List [str ]:
297+ # Add the additional plotly-resampler properties of this class
298+ return super ()._get_pr_props_keys () + ["_show_dash_kwargs" ]
299+
300+ def _ipython_display_ (self ):
301+ # To display the figure inline as a dash app
302+ self .show_dash (mode = "inline" )
0 commit comments