diff --git a/piker/ui/_curve.py b/piker/ui/_curve.py index ac967bf7..ac5d12ca 100644 --- a/piker/ui/_curve.py +++ b/piker/ui/_curve.py @@ -379,17 +379,17 @@ class Curve(pg.GraphicsObject): ) -> None: # default line draw last call - with self.reset_cache(): - x = render_data['index'] - y = render_data[array_key] + # with self.reset_cache(): + x = render_data['index'] + y = render_data[array_key] - # draw the "current" step graphic segment so it - # lines up with the "middle" of the current - # (OHLC) sample. - self._last_line = QLineF( - x[-2], y[-2], - x[-1], y[-1], - ) + # draw the "current" step graphic segment so it + # lines up with the "middle" of the current + # (OHLC) sample. + self._last_line = QLineF( + x[-2], y[-2], + x[-1], y[-1], + ) return x, y diff --git a/piker/ui/_display.py b/piker/ui/_display.py index 415827fb..9ad59e30 100644 --- a/piker/ui/_display.py +++ b/piker/ui/_display.py @@ -426,71 +426,6 @@ def graphics_update_cycle( profiler('view incremented') - if vlm_chart: - # always update y-label - ds.vlm_sticky.update_from_data( - *array[-1][['index', 'volume']] - ) - - if ( - ( - do_rt_update - or do_append - and liv - ) - or trigger_all - ): - # TODO: make it so this doesn't have to be called - # once the $vlm is up? - vlm_chart.update_graphics_from_flow( - 'volume', - # UGGGh, see ``maxmin()`` impl in `._fsp` for - # the overlayed plotitems... we need a better - # bay to invoke a maxmin per overlay.. - render=False, - # XXX: ^^^^ THIS IS SUPER IMPORTANT! ^^^^ - # without this, since we disable the - # 'volume' (units) chart after the $vlm starts - # up we need to be sure to enable this - # auto-ranging otherwise there will be no handler - # connected to update accompanying overlay - # graphics.. - ) - profiler('`vlm_chart.update_graphics_from_flow()`') - - if ( - mx_vlm_in_view != vars['last_mx_vlm'] - ): - yrange = (0, mx_vlm_in_view * 1.375) - vlm_chart.view._set_yrange( - yrange=yrange, - ) - profiler('`vlm_chart.view._set_yrange()`') - # print(f'mx vlm: {last_mx_vlm} -> {mx_vlm_in_view}') - vars['last_mx_vlm'] = mx_vlm_in_view - - for curve_name, flow in vlm_chart._flows.items(): - - if not flow.render: - continue - - update_fsp_chart( - vlm_chart, - flow, - curve_name, - array_key=curve_name, - # do_append=uppx < update_uppx, - do_append=do_append, - ) - # is this even doing anything? - # (pretty sure it's the real-time - # resizing from last quote?) - fvb = flow.plot.vb - fvb._set_yrange( - # autoscale_linked_plots=False, - name=curve_name, - ) - ticks_frame = quote.get('ticks', ()) frames_by_type: dict[str, dict] = {} @@ -540,15 +475,16 @@ def graphics_update_cycle( or do_append or trigger_all ): - # TODO: we should always update the "last" datum - # since the current range should at least be updated - # to it's max/min on the last pixel. chart.update_graphics_from_flow( chart.name, # do_append=uppx < update_uppx, do_append=do_append, ) + # NOTE: we always update the "last" datum + # since the current range should at least be updated + # to it's max/min on the last pixel. + # iterate in FIFO order per tick-frame for typ, tick in lasts.items(): @@ -653,30 +589,115 @@ def graphics_update_cycle( vars['last_mx'], vars['last_mn'] = mx, mn # run synchronous update on all linked flows + # TODO: should the "main" (aka source) flow be special? for curve_name, flow in chart._flows.items(): + # update any overlayed fsp flows + if curve_name != chart.data_key: + update_fsp_chart( + chart, + flow, + curve_name, + array_key=curve_name, + ) + + # even if we're downsampled bigly + # draw the last datum in the final + # px column to give the user the mx/mn + # range of that set. + if ( + not do_append + # and not do_rt_update + and liv + ): + flow.draw_last( + array_key=curve_name, + only_last_uppx=True, + ) + + # volume chart logic.. + # TODO: can we unify this with the above loop? + if vlm_chart: + # always update y-label + ds.vlm_sticky.update_from_data( + *array[-1][['index', 'volume']] + ) if ( - not (do_rt_update or do_append) - and liv - # even if we're downsampled bigly - # draw the last datum in the final - # px column to give the user the mx/mn - # range of that set. + ( + do_rt_update + or do_append + and liv + ) + or trigger_all ): - # always update the last datum-element - # graphic for all flows - flow.draw_last(array_key=curve_name) + # TODO: make it so this doesn't have to be called + # once the $vlm is up? + vlm_chart.update_graphics_from_flow( + 'volume', + # UGGGh, see ``maxmin()`` impl in `._fsp` for + # the overlayed plotitems... we need a better + # bay to invoke a maxmin per overlay.. + render=False, + # XXX: ^^^^ THIS IS SUPER IMPORTANT! ^^^^ + # without this, since we disable the + # 'volume' (units) chart after the $vlm starts + # up we need to be sure to enable this + # auto-ranging otherwise there will be no handler + # connected to update accompanying overlay + # graphics.. + ) + profiler('`vlm_chart.update_graphics_from_flow()`') - # TODO: should the "main" (aka source) flow be special? - if curve_name == chart.data_key: - continue + if ( + mx_vlm_in_view != vars['last_mx_vlm'] + ): + yrange = (0, mx_vlm_in_view * 1.375) + vlm_chart.view._set_yrange( + yrange=yrange, + ) + profiler('`vlm_chart.view._set_yrange()`') + # print(f'mx vlm: {last_mx_vlm} -> {mx_vlm_in_view}') + vars['last_mx_vlm'] = mx_vlm_in_view - update_fsp_chart( - chart, - flow, - curve_name, - array_key=curve_name, - ) + for curve_name, flow in vlm_chart._flows.items(): + + if ( + curve_name != 'volume' and + flow.render and ( + liv and + do_rt_update or do_append + ) + ): + update_fsp_chart( + vlm_chart, + flow, + curve_name, + array_key=curve_name, + # do_append=uppx < update_uppx, + do_append=do_append, + ) + # is this even doing anything? + # (pretty sure it's the real-time + # resizing from last quote?) + fvb = flow.plot.vb + fvb._set_yrange( + name=curve_name, + ) + + elif ( + curve_name != 'volume' + and not do_append + and liv + and uppx >= 1 + # even if we're downsampled bigly + # draw the last datum in the final + # px column to give the user the mx/mn + # range of that set. + ): + # always update the last datum-element + # graphic for all flows + # print(f'drawing last {flow.name}') + flow.draw_last(array_key=curve_name) async def display_symbol_data( diff --git a/piker/ui/_flows.py b/piker/ui/_flows.py index 01bbbece..01e7159b 100644 --- a/piker/ui/_flows.py +++ b/piker/ui/_flows.py @@ -175,6 +175,7 @@ def render_baritems( name=f'{flow.name}_ds_ohlc', color=bars._color, ) + flow.ds_graphics = curve curve.hide() self.plot.addItem(curve) @@ -192,18 +193,20 @@ def render_baritems( uppx = curve.x_uppx() in_line = should_line = curve.isVisible() if ( - should_line + in_line and uppx < x_gt ): # print('FLIPPING TO BARS') should_line = False + flow._in_ds = False elif ( - not should_line + not in_line and uppx >= x_gt ): # print('FLIPPING TO LINE') should_line = True + flow._in_ds = True profiler(f'ds logic complete line={should_line}') @@ -333,7 +336,13 @@ class Flow(msgspec.Struct): # , frozen=True): ''' name: str plot: pg.PlotItem - graphics: Curve + graphics: Union[Curve, BarItems] + + # in some cases a flow may want to change its + # graphical "type" or, "form" when downsampling, + # normally this is just a plain line. + ds_graphics: Optional[Curve] = None + _shm: ShmArray is_ohlc: bool = False @@ -540,6 +549,7 @@ class Flow(msgspec.Struct): # , frozen=True): should_redraw: bool = False rkwargs = {} + should_line = False if isinstance(graphics, BarItems): # XXX: special case where we change out graphics # to a line after a certain uppx threshold. @@ -556,8 +566,8 @@ class Flow(msgspec.Struct): # , frozen=True): profiler, **kwargs, ) - # bars = True should_redraw = changed_to_line or not should_line + self._in_ds = should_line else: r = self._src_r @@ -661,6 +671,17 @@ class Flow(msgspec.Struct): # , frozen=True): # assign output paths to graphicis obj graphics.path = r.path graphics.fast_path = r.fast_path + + # XXX: we don't need this right? + # graphics.draw_last_datum( + # path, + # src_array, + # data, + # reset, + # array_key, + # ) + # graphics.update() + # profiler('.update()') else: # assign output paths to graphicis obj graphics.path = r.path @@ -673,16 +694,15 @@ class Flow(msgspec.Struct): # , frozen=True): reset, array_key, ) - - # TODO: is this ever better? - # graphics.prepareGeometryChange() - # profiler('.prepareGeometryChange()') + graphics.update() + profiler('.update()') # TODO: does this actuallly help us in any way (prolly should # look at the source / ask ogi). I think it avoid artifacts on # wheel-scroll downsampling curve updates? - graphics.update() - profiler('.update()') + # TODO: is this ever better? + # graphics.prepareGeometryChange() + # profiler('.prepareGeometryChange()') # track downsampled state self._in_ds = r._in_ds @@ -692,6 +712,7 @@ class Flow(msgspec.Struct): # , frozen=True): def draw_last( self, array_key: Optional[str] = None, + only_last_uppx: bool = False, ) -> None: @@ -711,19 +732,34 @@ class Flow(msgspec.Struct): # , frozen=True): array_key, ) - if self._in_ds: - # we only care about the last pixel's - # worth of data since that's all the screen - # can represent on the last column where - # the most recent datum is being drawn. + # the renderer is downsampling we choose + # to always try and updadte a single (interpolating) + # line segment that spans and tries to display + # the las uppx's worth of datums. + # we only care about the last pixel's + # worth of data since that's all the screen + # can represent on the last column where + # the most recent datum is being drawn. + if self._in_ds or only_last_uppx: + dsg = self.ds_graphics or self.graphics + + # XXX: pretty sure we don't need this? + # if isinstance(g, Curve): + # with dsg.reset_cache(): uppx = self._last_uppx y = y[-uppx:] ymn, ymx = y.min(), y.max() # print(f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}') - g._last_line = QLineF( - x[-2], ymn, + dsg._last_line = QLineF( + x[-uppx], ymn, x[-1], ymx, ) + # print(f'updating DS curve {self.name}') + dsg.update() + + else: + # print(f'updating NOT DS curve {self.name}') + g.update() def by_index_and_key(