From 1770ceeacc87f791e9cb32bf4e881d3dc119defe Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 19 Jan 2023 10:20:29 -0500 Subject: [PATCH] Right, handle y-ranging multiple paths per plot We were hacking this before using the whole `ChartView._maxmin()` setting stuff since in some cases you might want similarly ranged paths on the same view, but of course you need to max/min them together.. This adds that group sorting by using a table of `dict[PlotItem, tuple[float, float]` and taking the abs highest/lowest value for each plot in the viz interaction update loop. Also removes the now commented signal registry calls and thus `._yranger`, drops the `set_range: bool` from `._set_yrange` and adds and extra `.maybe_downsample_graphics()` to the mouse wheel handler to avoid a weird slow debounce where ds-ing is delayed until a further interaction. --- piker/ui/_interaction.py | 170 +++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 71 deletions(-) diff --git a/piker/ui/_interaction.py b/piker/ui/_interaction.py index 18b485f0..6ef290b7 100644 --- a/piker/ui/_interaction.py +++ b/piker/ui/_interaction.py @@ -20,7 +20,6 @@ Chart view box primitives """ from __future__ import annotations from contextlib import asynccontextmanager -from functools import partial import time from typing import ( Optional, @@ -393,7 +392,6 @@ class ChartView(ViewBox): self.setFocusPolicy(QtCore.Qt.StrongFocus) self._ic = None - self._yranger: Callable | None = None # TODO: probably just assign this whenever a new `PlotItem` is # allocated since they're 1to1 with views.. @@ -568,6 +566,7 @@ class ChartView(ViewBox): # "double work" is causing latency when these missing event # fires don't happen? self.maybe_downsample_graphics() + self.maybe_downsample_graphics() ev.accept() @@ -763,7 +762,6 @@ class ChartView(ViewBox): ms_threshold=ms_slower_then, delayed=True, ) - set_range = True chart = self._chart # view has been set in 'axis' mode @@ -772,8 +770,8 @@ class ChartView(ViewBox): # - disable autoranging # - remove any y range limits if chart._static_yrange == 'axis': - set_range = False self.setLimits(yMin=None, yMax=None) + return # static y-range has been set likely by # a specialized FSP configuration. @@ -786,48 +784,63 @@ class ChartView(ViewBox): elif yrange is not None: ylow, yhigh = yrange - if set_range: + # XXX: only compute the mxmn range + # if none is provided as input! + if not yrange: - # XXX: only compute the mxmn range - # if none is provided as input! - if not yrange: + if not viz: + breakpoint() - if not viz: - breakpoint() + out = viz.maxmin() + if out is None: + log.warning(f'No yrange provided for {name}!?') + return + ( + ixrng, + _, + yrange + ) = out - out = viz.maxmin() - if out is None: - log.warning(f'No yrange provided for {name}!?') - return - ( - ixrng, - _, - yrange - ) = out + profiler(f'`{self.name}:Viz.maxmin()` -> {ixrng}=>{yrange}') - profiler(f'`{self.name}:Viz.maxmin()` -> {ixrng}=>{yrange}') - - if yrange is None: - log.warning(f'No yrange provided for {name}!?') - return + if yrange is None: + log.warning(f'No yrange provided for {name}!?') + return ylow, yhigh = yrange - # view margins: stay within a % of the "true range" - diff = yhigh - ylow - ylow = ylow - (diff * range_margin) - yhigh = yhigh + (diff * range_margin) + # view margins: stay within a % of the "true range" + diff = yhigh - ylow + ylow = max( + ylow - (diff * range_margin), + 0, + ) + yhigh = min( + yhigh + (diff * range_margin), + yhigh * (1 + range_margin), + ) - # XXX: this often needs to be unset - # to get different view modes to operate - # correctly! - self.setLimits( - yMin=ylow, - yMax=yhigh, - ) - self.setYRange(ylow, yhigh) - profiler(f'set limits: {(ylow, yhigh)}') + # XXX: this often needs to be unset + # to get different view modes to operate + # correctly! + profiler( + f'set limits {self.name}:\n' + f'ylow: {ylow}\n' + f'yhigh: {yhigh}\n' + ) + self.setYRange( + ylow, + yhigh, + padding=0, + ) + self.setLimits( + yMin=ylow, + yMax=yhigh, + ) + + # LOL: yet anothercucking pg buggg.. + # can't use `msg=f'setYRange({ylow}, {yhigh}')` profiler.finish() def enable_auto_yrange( @@ -844,12 +857,6 @@ class ChartView(ViewBox): if src_vb is None: src_vb = self - if self._yranger is None: - self._yranger = partial( - self._set_yrange, - viz=viz, - ) - # re-sampling trigger: # TODO: a smarter way to avoid calling this needlessly? # 2 things i can think of: @@ -866,25 +873,13 @@ class ChartView(ViewBox): self.maybe_downsample_graphics ) - # mouse wheel doesn't emit XRangeChanged - # src_vb.sigRangeChangedManually.connect(self._yranger) - def disable_auto_yrange(self) -> None: # XXX: not entirely sure why we can't de-reg this.. self.sigResized.disconnect( - # self._yranger, self.maybe_downsample_graphics ) - # self.sigRangeChangedManually.disconnect( - # self._yranger, - # ) - - # self.sigRangeChangedManually.disconnect( - # self.maybe_downsample_graphics - # ) - def x_uppx(self) -> float: ''' Return the "number of x units" within a single @@ -908,14 +903,18 @@ class ChartView(ViewBox): ): profiler = Profiler( msg=f'ChartView.maybe_downsample_graphics() for {self.name}', - disabled=not pg_profile_enabled(), + # disabled=not pg_profile_enabled(), + + # ms_threshold=ms_slower_then, + + disabled=True, + ms_threshold=4, # XXX: important to avoid not seeing underlying # ``.update_graphics_from_flow()`` nested profiling likely # due to the way delaying works and garbage collection of # the profiler in the delegated method calls. - ms_threshold=6, - # ms_threshold=ms_slower_then, + delayed=True, ) # TODO: a faster single-loop-iterator way of doing this XD @@ -927,20 +926,22 @@ class ChartView(ViewBox): plots |= linked.subplots for chart_name, chart in plots.items(): + + mxmns: dict[ + pg.PlotItem, + tuple[float, float], + ] = {} + for name, viz in chart._vizs.items(): - - if ( - not viz.render - - # XXX: super important to be aware of this. - # or not flow.graphics.isVisible() - ): + if not viz.render: # print(f'skipping {flow.name}') continue # pass in no array which will read and render from the last # passed array (normally provided by the display loop.) i_read_range, _ = viz.update_graphics() + profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`') + out = viz.maxmin(i_read_range=i_read_range) if out is None: log.warning(f'No yrange provided for {name}!?') @@ -951,13 +952,40 @@ class ChartView(ViewBox): yrange ) = out - # print( - # f'i_read_range: {i_read_range}\n' - # f'ixrng: {ixrng}\n' - # f'yrange: {yrange}\n' - # ) - viz.plot.vb._set_yrange(yrange=yrange) + pi = viz.plot + mxmn = mxmns.get(pi) + if mxmn: + yrange = mxmns[pi] = ( + min(yrange[0], mxmn[0]), + max(yrange[1], mxmn[1]), + ) + + else: + mxmns[viz.plot] = yrange + + pi.vb._set_yrange(yrange=yrange) + profiler( + f'{viz.name}@{chart_name} `Viz.plot.vb._set_yrange()`' + ) + + # if 'dolla_vlm' in viz.name: + # print( + # f'AUTO-Y-RANGING: {viz.name}\n' + # f'i_read_range: {i_read_range}\n' + # f'ixrng: {ixrng}\n' + # f'yrange: {yrange}\n' + # ) + # ( + # view_xrange, + # view_yrange, + # ) = viz.plot.vb.viewRange() + # print( + # f'{viz.name}@{chart_name}\n' + # f' xRange -> {view_xrange}\n' + # f' yRange -> {view_yrange}\n' + # ) profiler(f'autoscaled overlays {chart_name}') profiler(f'<{chart_name}>.update_graphics_from_flow({name})') + profiler.finish()