From 9d3de6ec02429158a22af4a6924683f834f32859 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sat, 14 Jan 2023 16:11:25 -0500 Subject: [PATCH] Drop x-range query from `ChartPlotWidget.maxmin()` Move the `Viz.datums_range()` call into `Viz.maxmin()` itself thus minimizing the chart `.maxmin()` method to an ultra light wrapper around the viz call. Also move all profiling into the `Viz` method. Adjust `Viz.maxmin()` to return both the (rounded) x-range values which correspond to the range containing the y-domain min and max so that it can be used for up and coming overlay group maxmin calcs. --- piker/ui/_chart.py | 61 +++++++++-------------------- piker/ui/_dataviz.py | 91 ++++++++++++++++++++++++++++++++------------ 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/piker/ui/_chart.py b/piker/ui/_chart.py index 8eb35267..4f534c85 100644 --- a/piker/ui/_chart.py +++ b/piker/ui/_chart.py @@ -50,7 +50,6 @@ from ._cursor import ( ContentsLabel, ) from ..data._sharedmem import ShmArray -from ._l1 import L1Labels from ._ohlc import BarItems from ._curve import ( Curve, @@ -70,12 +69,10 @@ from ..data._source import Symbol from ..log import get_logger from ._interaction import ChartView from ._forms import FieldsForm -from .._profile import pg_profile_enabled, ms_slower_then from ._overlay import PlotItemOverlay from ._dataviz import Viz from ._search import SearchWidget from . import _pg_overrides as pgo -from .._profile import Profiler if TYPE_CHECKING: from ._display import DisplayState @@ -857,17 +854,17 @@ class ChartPlotWidget(pg.PlotWidget): self.sidepane: Optional[FieldsForm] = None # source of our custom interactions - self.cv = cv = self.mk_vb(name) + self.cv = self.mk_vb(name) pi = pgo.PlotItem( - viewBox=cv, + viewBox=self.cv, name=name, **kwargs, ) pi.chart_widget = self super().__init__( background=hcolor(view_color), - viewBox=cv, + viewBox=self.cv, # parent=None, # plotItem=None, # antialias=True, @@ -878,7 +875,9 @@ class ChartPlotWidget(pg.PlotWidget): # give viewbox as reference to chart # allowing for kb controls and interactions on **this** widget # (see our custom view mode in `._interactions.py`) - cv.chart = self + self.cv.chart = self + + self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem) # ensure internal pi matches assert self.cv is self.plotItem.vb @@ -907,8 +906,6 @@ class ChartPlotWidget(pg.PlotWidget): # show background grid self.showGrid(x=False, y=True, alpha=0.3) - self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem) - # indempotent startup flag for auto-yrange subsys # to detect the "first time" y-domain graphics begin # to be shown in the (main) graphics view. @@ -1302,13 +1299,6 @@ class ChartPlotWidget(pg.PlotWidget): If ``bars_range`` is provided use that range. ''' - profiler = Profiler( - msg=f'`{str(self)}.maxmin(name={name})`: `{self.name}`', - disabled=not pg_profile_enabled(), - ms_threshold=ms_slower_then, - delayed=True, - ) - # TODO: here we should instead look up the ``Viz.shm.array`` # and read directly from shm to avoid copying to memory first # and then reading it again here. @@ -1316,36 +1306,21 @@ class ChartPlotWidget(pg.PlotWidget): viz = self._vizs.get(viz_key) if viz is None: log.error(f"viz {viz_key} doesn't exist in chart {self.name} !?") - key = res = 0, 0 + return 0, 0 + res = viz.maxmin() + + if ( + res is None + ): + mxmn = 0, 0 + if not self._on_screen: + self.default_view(do_ds=False) + self._on_screen = True else: - ( - l, - _, - lbar, - rbar, - _, - r, - ) = bars_range or viz.datums_range() + x_range, mxmn = res - profiler(f'{self.name} got bars range') - key = lbar, rbar - res = viz.maxmin(*key) - - if ( - res is None - ): - log.warning( - f"{viz_key} no mxmn for bars_range => {key} !?" - ) - res = 0, 0 - if not self._on_screen: - self.default_view(do_ds=False) - self._on_screen = True - - profiler(f'yrange mxmn: {key} -> {res}') - # print(f'{viz_key} yrange mxmn: {key} -> {res}') - return res + return mxmn def get_viz( self, diff --git a/piker/ui/_dataviz.py b/piker/ui/_dataviz.py index bc50f04c..82e60837 100644 --- a/piker/ui/_dataviz.py +++ b/piker/ui/_dataviz.py @@ -60,6 +60,7 @@ from ..log import get_logger from .._profile import ( Profiler, pg_profile_enabled, + ms_slower_then, ) @@ -276,7 +277,10 @@ class Viz(msgspec.Struct): # , frozen=True): ] = (None, None) # cache of y-range values per x-range input. - _mxmns: dict[tuple[int, int], tuple[float, float]] = {} + _mxmns: dict[ + tuple[int, int], + tuple[float, float], + ] = {} @property def shm(self) -> ShmArray: @@ -324,32 +328,29 @@ class Viz(msgspec.Struct): # , frozen=True): def maxmin( self, - lbar: int, - rbar: int, + # TODO: drop this right? + bars_range: Optional[tuple[ + int, int, int, int, int, int + ]] = None, + + x_range: slice | tuple[int, int] | None = None, use_caching: bool = True, - ) -> Optional[tuple[float, float]]: + ) -> tuple[float, float] | None: ''' Compute the cached max and min y-range values for a given x-range determined by ``lbar`` and ``rbar`` or ``None`` if no range can be determined (yet). ''' - # TODO: hash the slice instead maybe? - # https://stackoverflow.com/a/29980872 - rkey = (round(lbar), round(rbar)) - - do_print: bool = False - if use_caching: - cached_result = self._mxmns.get(rkey) - if cached_result: - if do_print: - print( - f'{self.name} CACHED maxmin\n' - f'{rkey} -> {cached_result}' - ) - return cached_result + name = self.name + profiler = Profiler( + msg=f'{name} -> `{str(self)}.maxmin()`', + disabled=not pg_profile_enabled(), + ms_threshold=ms_slower_then, + delayed=True, + ) shm = self.shm if shm is None: @@ -357,6 +358,41 @@ class Viz(msgspec.Struct): # , frozen=True): arr = shm.array + if x_range is None: + ( + l, + _, + lbar, + rbar, + _, + r, + ) = ( + # TODO: drop this yah? + bars_range + or self.datums_range() + ) + + profiler(f'{self.name} got bars range') + x_range = lbar, rbar + + # TODO: hash the slice instead maybe? + # https://stackoverflow.com/a/29980872 + ixrng = (round(lbar), round(rbar)) + + do_print: bool = False + if use_caching: + cached_result = self._mxmns.get(ixrng) + if cached_result: + if do_print: + print( + f'{self.name} CACHED maxmin\n' + f'{ixrng} -> {cached_result}' + ) + return ( + ixrng, + cached_result, + ) + # get relative slice indexes into array if self.index_field == 'time': read_slc = slice_from_time( @@ -376,7 +412,10 @@ class Viz(msgspec.Struct): # , frozen=True): slice_view = arr[read_slc] if not slice_view.size: - log.warning(f'{self.name} no maxmin in view?') + log.warning( + f'{self.name} no maxmin in view?\n' + f"{name} no mxmn for bars_range => {ixrng} !?" + ) return None elif self.yrange: @@ -384,9 +423,8 @@ class Viz(msgspec.Struct): # , frozen=True): if do_print: print( f'{self.name} M4 maxmin:\n' - f'{rkey} -> {mxmn}' + f'{ixrng} -> {mxmn}' ) - else: if self.is_ohlc: ylow = np.min(slice_view['low']) @@ -404,7 +442,7 @@ class Viz(msgspec.Struct): # , frozen=True): s = 3 print( f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n' - f'{rkey} -> {mxmn}\n' + f'{ixrng} -> {mxmn}\n' f'read_slc: {read_slc}\n' # f'abs_slc: {slice_view["index"]}\n' f'first {s}:\n{slice_view[:s]}\n' @@ -413,9 +451,12 @@ class Viz(msgspec.Struct): # , frozen=True): # cache result for input range assert mxmn - self._mxmns[rkey] = mxmn - - return mxmn + self._mxmns[ixrng] = mxmn + profiler(f'yrange mxmn cacheing: {x_range} -> {mxmn}') + return ( + ixrng, + mxmn, + ) def view_range(self) -> tuple[int, int]: '''