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`.multichartz
parent
e9201c2bdf
commit
de3fd9edbe
|
@ -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