From ccb13bcb3d4066dd1d6abc822d3f0ea5d4ef234e Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 26 Dec 2022 14:46:46 -0500 Subject: [PATCH] Add a parent-type for graphics: `FlowGraphic` Factor some common methods into the parent type: - `.x_uppx()` for reading the horizontal units-per-pixel. - `.x_last()` for reading the "closest to y-axis" last datum coordinate for zooming "around" during mouse interaction. - `.px_width()` for computing the max width of any curve in view in pixels. Adjust all previous derived `pg.GraphicsObject` child types to now inherit from this new parent and in particular enable proper `.x_uppx()` support to `BarItems`. --- piker/ui/_curve.py | 126 ++++++++++++++++++++++++--------------------- piker/ui/_ohlc.py | 9 ++-- 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/piker/ui/_curve.py b/piker/ui/_curve.py index b5d128d6..f22dcd14 100644 --- a/piker/ui/_curve.py +++ b/piker/ui/_curve.py @@ -51,7 +51,59 @@ _line_styles: dict[str, int] = { } -class Curve(pg.GraphicsObject): +class FlowGraphic(pg.GraphicsObject): + ''' + Base class with minimal interface for `QPainterPath` implemented, + real-time updated "data flow" graphics. + + See subtypes below. + + ''' + # sub-type customization methods + declare_paintables: Optional[Callable] = None + sub_paint: Optional[Callable] = None + + # TODO: can we remove this? + # sub_br: Optional[Callable] = None + + def x_uppx(self) -> int: + + px_vecs = self.pixelVectors()[0] + if px_vecs: + xs_in_px = px_vecs.x() + return round(xs_in_px) + else: + return 0 + + def x_last(self) -> float: + ''' + Return the last most x value of the last line segment. + + ''' + return self._last_line.x1() + + def px_width(self) -> float: + ''' + Return the width of the view box containing + this graphic in pixel units. + + ''' + vb = self.getViewBox() + if not vb: + return 0 + + vr = self.viewRect() + vl, vr = int(vr.left()), int(vr.right()) + + return vb.mapViewToDevice( + QLineF( + vl, 0, + vr, 0, + ) + ).length() + + +class Curve(FlowGraphic): ''' A faster, simpler, append friendly version of ``pyqtgraph.PlotCurveItem`` built for highly customizable real-time @@ -81,11 +133,6 @@ class Curve(pg.GraphicsObject): ''' - # sub-type customization methods - declare_paintables: Optional[Callable] = None - sub_paint: Optional[Callable] = None - # sub_br: Optional[Callable] = None - def __init__( self, *args, @@ -95,7 +142,6 @@ class Curve(pg.GraphicsObject): fill_color: Optional[str] = None, style: str = 'solid', name: Optional[str] = None, - use_fpath: bool = True, **kwargs @@ -110,11 +156,11 @@ class Curve(pg.GraphicsObject): # self._last_cap: int = 0 self.path: Optional[QPainterPath] = None - # additional path used for appends which tries to avoid - # triggering an update/redraw of the presumably larger - # historical ``.path`` above. - self.use_fpath = use_fpath - self.fast_path: Optional[QPainterPath] = None + # additional path that can be optionally used for appends which + # tries to avoid triggering an update/redraw of the presumably + # larger historical ``.path`` above. the flag to enable + # this behaviour is found in `Renderer.render()`. + self.fast_path: QPainterPath | None = None # TODO: we can probably just dispense with the parent since # we're basically only using the pen setting now... @@ -154,58 +200,19 @@ class Curve(pg.GraphicsObject): # endpoint (something we saw on trade rate curves) self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) - # XXX: see explanation for different caching modes: - # https://stackoverflow.com/a/39410081 - # seems to only be useful if we don't re-generate the entire - # QPainterPath every time - # curve.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache) - + # XXX-NOTE-XXX: graphics caching. + # see explanation for different caching modes: + # https://stackoverflow.com/a/39410081 seems to only be useful + # if we don't re-generate the entire QPainterPath every time # don't ever use this - it's a colossal nightmare of artefacts # and is disastrous for performance. - # curve.setCacheMode(QtWidgets.QGraphicsItem.ItemCoordinateCache) + # self.setCacheMode(QtWidgets.QGraphicsItem.ItemCoordinateCache) # allow sub-type customization declare = self.declare_paintables if declare: declare() - # TODO: probably stick this in a new parent - # type which will contain our own version of - # what ``PlotCurveItem`` had in terms of base - # functionality? A `FlowGraphic` maybe? - def x_uppx(self) -> int: - - px_vecs = self.pixelVectors()[0] - if px_vecs: - xs_in_px = px_vecs.x() - return round(xs_in_px) - else: - return 0 - - def x_last(self) -> float: - ''' - Return the last most x value of the last line segment. - - ''' - return self._last_line.x2() - - def px_width(self) -> float: - - vb = self.getViewBox() - if not vb: - return 0 - - vr = self.viewRect() - l, r = int(vr.left()), int(vr.right()) - - start, stop = self._xrange - lbar = max(l, start) - rbar = min(r, stop) - - return vb.mapViewToDevice( - QLineF(lbar, 0, rbar, 0) - ).length() - # XXX: lol brutal, the internals of `CurvePoint` (inherited by # our `LineDot`) required ``.getData()`` to work.. def getData(self): @@ -370,6 +377,9 @@ class Curve(pg.GraphicsObject): x = src_data[index_field] y = src_data[array_key] + x_last = x[-1] + x_2last = x[-2] + # draw the "current" step graphic segment so it # lines up with the "middle" of the current # (OHLC) sample. @@ -379,8 +389,8 @@ class Curve(pg.GraphicsObject): # from last datum to current such that # the end of line touches the "beginning" # of the current datum step span. - x[-2], y[-2], - x[-1], y[-1], + x_2last , y[-2], + x_last, y[-1], ) return x, y diff --git a/piker/ui/_ohlc.py b/piker/ui/_ohlc.py index 288af70d..9712bb9d 100644 --- a/piker/ui/_ohlc.py +++ b/piker/ui/_ohlc.py @@ -36,6 +36,7 @@ from PyQt5.QtCore import ( from PyQt5.QtGui import QPainterPath +from ._curve import FlowGraphic from .._profile import pg_profile_enabled, ms_slower_then from ._style import hcolor from ..log import get_logger @@ -94,7 +95,7 @@ def bar_from_ohlc_row( return [hl, o, c] -class BarItems(pg.GraphicsObject): +class BarItems(FlowGraphic): ''' "Price range" bars graphics rendered from a OHLC sampled sequence. @@ -125,9 +126,9 @@ class BarItems(pg.GraphicsObject): self.path = QPainterPath() self._last_bar_lines: tuple[QLineF, ...] | None = None - def x_uppx(self) -> int: - # we expect the downsample curve report this. - return 0 + # def x_uppx(self) -> int: + # # we expect the downsample curve report this. + # return 0 def x_last(self) -> float: '''