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`.epoch_index_backup
							parent
							
								
									b32cb7ecad
								
							
						
					
					
						commit
						ef19604698
					
				| 
						 | 
					@ -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
 | 
					    A faster, simpler, append friendly version of
 | 
				
			||||||
    ``pyqtgraph.PlotCurveItem`` built for highly customizable real-time
 | 
					    ``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__(
 | 
					    def __init__(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        *args,
 | 
					        *args,
 | 
				
			||||||
| 
						 | 
					@ -95,7 +142,6 @@ class Curve(pg.GraphicsObject):
 | 
				
			||||||
        fill_color: Optional[str] = None,
 | 
					        fill_color: Optional[str] = None,
 | 
				
			||||||
        style: str = 'solid',
 | 
					        style: str = 'solid',
 | 
				
			||||||
        name: Optional[str] = None,
 | 
					        name: Optional[str] = None,
 | 
				
			||||||
        use_fpath: bool = True,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        **kwargs
 | 
					        **kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,11 +156,11 @@ class Curve(pg.GraphicsObject):
 | 
				
			||||||
        # self._last_cap: int = 0
 | 
					        # self._last_cap: int = 0
 | 
				
			||||||
        self.path: Optional[QPainterPath] = None
 | 
					        self.path: Optional[QPainterPath] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # additional path used for appends which tries to avoid
 | 
					        # additional path that can be optionally used for appends which
 | 
				
			||||||
        # triggering an update/redraw of the presumably larger
 | 
					        # tries to avoid triggering an update/redraw of the presumably
 | 
				
			||||||
        # historical ``.path`` above.
 | 
					        # larger historical ``.path`` above. the flag to enable
 | 
				
			||||||
        self.use_fpath = use_fpath
 | 
					        # this behaviour is found in `Renderer.render()`.
 | 
				
			||||||
        self.fast_path: Optional[QPainterPath] = None
 | 
					        self.fast_path: QPainterPath | None = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO: we can probably just dispense with the parent since
 | 
					        # TODO: we can probably just dispense with the parent since
 | 
				
			||||||
        # we're basically only using the pen setting now...
 | 
					        # 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)
 | 
					        # endpoint (something we saw on trade rate curves)
 | 
				
			||||||
        self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
 | 
					        self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # XXX: see explanation for different caching modes:
 | 
					        # XXX-NOTE-XXX: graphics caching.
 | 
				
			||||||
        # https://stackoverflow.com/a/39410081
 | 
					        # see explanation for different caching modes:
 | 
				
			||||||
        # seems to only be useful if we don't re-generate the entire
 | 
					        # https://stackoverflow.com/a/39410081 seems to only be useful
 | 
				
			||||||
        # QPainterPath every time
 | 
					        # if we don't re-generate the entire QPainterPath every time
 | 
				
			||||||
        # curve.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # don't ever use this - it's a colossal nightmare of artefacts
 | 
					        # don't ever use this - it's a colossal nightmare of artefacts
 | 
				
			||||||
        # and is disastrous for performance.
 | 
					        # and is disastrous for performance.
 | 
				
			||||||
        # curve.setCacheMode(QtWidgets.QGraphicsItem.ItemCoordinateCache)
 | 
					        # self.setCacheMode(QtWidgets.QGraphicsItem.ItemCoordinateCache)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # allow sub-type customization
 | 
					        # allow sub-type customization
 | 
				
			||||||
        declare = self.declare_paintables
 | 
					        declare = self.declare_paintables
 | 
				
			||||||
        if declare:
 | 
					        if declare:
 | 
				
			||||||
            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
 | 
					    # XXX: lol brutal, the internals of `CurvePoint` (inherited by
 | 
				
			||||||
    # our `LineDot`) required ``.getData()`` to work..
 | 
					    # our `LineDot`) required ``.getData()`` to work..
 | 
				
			||||||
    def getData(self):
 | 
					    def getData(self):
 | 
				
			||||||
| 
						 | 
					@ -370,6 +377,9 @@ class Curve(pg.GraphicsObject):
 | 
				
			||||||
        x = src_data[index_field]
 | 
					        x = src_data[index_field]
 | 
				
			||||||
        y = src_data[array_key]
 | 
					        y = src_data[array_key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        x_last = x[-1]
 | 
				
			||||||
 | 
					        x_2last = x[-2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # draw the "current" step graphic segment so it
 | 
					        # draw the "current" step graphic segment so it
 | 
				
			||||||
        # lines up with the "middle" of the current
 | 
					        # lines up with the "middle" of the current
 | 
				
			||||||
        # (OHLC) sample.
 | 
					        # (OHLC) sample.
 | 
				
			||||||
| 
						 | 
					@ -379,8 +389,8 @@ class Curve(pg.GraphicsObject):
 | 
				
			||||||
            # from last datum to current such that
 | 
					            # from last datum to current such that
 | 
				
			||||||
            # the end of line touches the "beginning"
 | 
					            # the end of line touches the "beginning"
 | 
				
			||||||
            # of the current datum step span.
 | 
					            # of the current datum step span.
 | 
				
			||||||
            x[-2], y[-2],
 | 
					            x_2last , y[-2],
 | 
				
			||||||
            x[-1], y[-1],
 | 
					            x_last, y[-1],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return x, y
 | 
					        return x, y
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,7 @@ from PyQt5.QtCore import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt5.QtGui import QPainterPath
 | 
					from PyQt5.QtGui import QPainterPath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._curve import FlowGraphic
 | 
				
			||||||
from .._profile import pg_profile_enabled, ms_slower_then
 | 
					from .._profile import pg_profile_enabled, ms_slower_then
 | 
				
			||||||
from ._style import hcolor
 | 
					from ._style import hcolor
 | 
				
			||||||
from ..log import get_logger
 | 
					from ..log import get_logger
 | 
				
			||||||
| 
						 | 
					@ -94,7 +95,7 @@ def bar_from_ohlc_row(
 | 
				
			||||||
    return [hl, o, c]
 | 
					    return [hl, o, c]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BarItems(pg.GraphicsObject):
 | 
					class BarItems(FlowGraphic):
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    "Price range" bars graphics rendered from a OHLC sampled sequence.
 | 
					    "Price range" bars graphics rendered from a OHLC sampled sequence.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,9 +126,9 @@ class BarItems(pg.GraphicsObject):
 | 
				
			||||||
        self.path = QPainterPath()
 | 
					        self.path = QPainterPath()
 | 
				
			||||||
        self._last_bar_lines: tuple[QLineF, ...] | None = None
 | 
					        self._last_bar_lines: tuple[QLineF, ...] | None = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def x_uppx(self) -> int:
 | 
					    # def x_uppx(self) -> int:
 | 
				
			||||||
        # we expect the downsample curve report this.
 | 
					    #     # we expect the downsample curve report this.
 | 
				
			||||||
        return 0
 | 
					    #     return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def x_last(self) -> float:
 | 
					    def x_last(self) -> float:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue