Factor color and cache mode settings into `FlowGraphics`
Curve-path colouring and cache mode settings are used (and can thus be factored out of) all child types; this moves them into the parent type's `.__init__()` and adjusts all sub-types match: - the bulk was moved out of the `Curve.__init__()` including all previous commentary around cache settings. - adjust `BarItems` to use a `NoCache` mode and instead use the `last_step_pen: pg.Pen` and `._pen` inside it's `.pain()` instead of defining functionally duplicate vars. - adjust all (transitive) calls to `BarItems` to use the new kwargs names.overlays_interaction_latency_tuning
parent
33df4f9927
commit
a36d4b1dc6
|
@ -1152,8 +1152,6 @@ class ChartPlotWidget(pg.PlotWidget):
|
||||||
|
|
||||||
if is_ohlc:
|
if is_ohlc:
|
||||||
graphics = BarItems(
|
graphics = BarItems(
|
||||||
linked=self.linked,
|
|
||||||
plotitem=pi,
|
|
||||||
color=color,
|
color=color,
|
||||||
name=name,
|
name=name,
|
||||||
**graphics_kwargs,
|
**graphics_kwargs,
|
||||||
|
|
|
@ -60,11 +60,82 @@ class FlowGraphic(pg.GraphicsObject):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# sub-type customization methods
|
# sub-type customization methods
|
||||||
declare_paintables: Optional[Callable] = None
|
declare_paintables: Callable | None = None
|
||||||
sub_paint: Optional[Callable] = None
|
sub_paint: Callable | None = None
|
||||||
|
|
||||||
# TODO: can we remove this?
|
# XXX-NOTE-XXX: graphics caching B)
|
||||||
# sub_br: Optional[Callable] = None
|
# see explanation for different caching modes:
|
||||||
|
# https://stackoverflow.com/a/39410081
|
||||||
|
cache_mode: int = QGraphicsItem.DeviceCoordinateCache
|
||||||
|
# XXX: WARNING item caching 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.
|
||||||
|
# QGraphicsItem.ItemCoordinateCache
|
||||||
|
# TODO: still questions todo with coord-cacheing that we should
|
||||||
|
# probably talk to a core dev about:
|
||||||
|
# - if this makes trasform interactions slower (such as zooming)
|
||||||
|
# and if so maybe if/when we implement a "history" mode for the
|
||||||
|
# view we disable this in that mode?
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
name: str | None = None,
|
||||||
|
|
||||||
|
# line styling
|
||||||
|
color: str = 'bracket',
|
||||||
|
last_step_color: str = 'original',
|
||||||
|
fill_color: Optional[str] = None,
|
||||||
|
style: str = 'solid',
|
||||||
|
|
||||||
|
**kwargs
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
# primary graphics item used for history
|
||||||
|
self.path: QPainterPath = QPainterPath()
|
||||||
|
|
||||||
|
# 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: evaluating the path capacity stuff and see
|
||||||
|
# if it really makes much diff pre-allocating it.
|
||||||
|
# self._last_cap: int = 0
|
||||||
|
# cap = path.capacity()
|
||||||
|
# if cap != self._last_cap:
|
||||||
|
# print(f'NEW CAPACITY: {self._last_cap} -> {cap}')
|
||||||
|
# self._last_cap = cap
|
||||||
|
|
||||||
|
# all history of curve is drawn in single px thickness
|
||||||
|
self._color: str = color
|
||||||
|
pen = pg.mkPen(hcolor(color), width=1)
|
||||||
|
pen.setStyle(_line_styles[style])
|
||||||
|
|
||||||
|
if 'dash' in style:
|
||||||
|
pen.setDashPattern([8, 3])
|
||||||
|
|
||||||
|
self._pen = pen
|
||||||
|
self._brush = pg.functions.mkBrush(
|
||||||
|
hcolor(fill_color or color)
|
||||||
|
)
|
||||||
|
|
||||||
|
# last segment is drawn in 2px thickness for emphasis
|
||||||
|
self.last_step_pen = pg.mkPen(
|
||||||
|
hcolor(last_step_color),
|
||||||
|
width=2,
|
||||||
|
)
|
||||||
|
self._last_line: QLineF = QLineF()
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# apply cache mode
|
||||||
|
self.setCacheMode(self.cache_mode)
|
||||||
|
|
||||||
def x_uppx(self) -> int:
|
def x_uppx(self) -> int:
|
||||||
|
|
||||||
|
@ -112,82 +183,32 @@ class Curve(FlowGraphic):
|
||||||
updates don't trigger a full path redraw.
|
updates don't trigger a full path redraw.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
cache_mode: int = QGraphicsItem.DeviceCoordinateCache
|
# TODO: can we remove this?
|
||||||
|
# sub_br: Optional[Callable] = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
|
|
||||||
step_mode: bool = False,
|
# color: str = 'default_lightest',
|
||||||
color: str = 'default_lightest',
|
# fill_color: Optional[str] = None,
|
||||||
fill_color: Optional[str] = None,
|
# style: str = 'solid',
|
||||||
style: str = 'solid',
|
|
||||||
name: Optional[str] = None,
|
|
||||||
|
|
||||||
**kwargs
|
**kwargs
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
# brutaaalll, see comments within..
|
# brutaaalll, see comments within..
|
||||||
self.yData = None
|
self.yData = None
|
||||||
self.xData = None
|
self.xData = None
|
||||||
|
|
||||||
# self._last_cap: int = 0
|
|
||||||
self.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
|
# 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...
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# all history of curve is drawn in single px thickness
|
|
||||||
pen = pg.mkPen(hcolor(color))
|
|
||||||
pen.setStyle(_line_styles[style])
|
|
||||||
|
|
||||||
if 'dash' in style:
|
|
||||||
pen.setDashPattern([8, 3])
|
|
||||||
|
|
||||||
self._pen = pen
|
|
||||||
|
|
||||||
# last segment is drawn in 2px thickness for emphasis
|
|
||||||
# self.last_step_pen = pg.mkPen(hcolor(color), width=2)
|
|
||||||
self.last_step_pen = pg.mkPen(pen, width=2)
|
|
||||||
|
|
||||||
self._last_line: QLineF = QLineF()
|
self._last_line: QLineF = QLineF()
|
||||||
|
|
||||||
# flat-top style histogram-like discrete curve
|
|
||||||
# self._step_mode: bool = step_mode
|
|
||||||
|
|
||||||
# self._fill = True
|
# self._fill = True
|
||||||
self._brush = pg.functions.mkBrush(hcolor(fill_color or color))
|
|
||||||
|
|
||||||
# NOTE: this setting seems to mostly prevent redraws on mouse
|
|
||||||
# interaction which is a huge boon for avg interaction latency.
|
|
||||||
|
|
||||||
# TODO: one question still remaining is if this makes trasform
|
|
||||||
# interactions slower (such as zooming) and if so maybe if/when
|
|
||||||
# we implement a "history" mode for the view we disable this in
|
|
||||||
# that mode?
|
|
||||||
# don't enable caching by default for the case where the
|
|
||||||
# only thing drawn is the "last" line segment which can
|
|
||||||
# have a weird artifact where it won't be fully drawn to its
|
|
||||||
# endpoint (something we saw on trade rate curves)
|
|
||||||
self.setCacheMode(self.cache_mode)
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
# self.setCacheMode(QtWidgets.QGraphicsItem.ItemCoordinateCache)
|
|
||||||
|
|
||||||
# allow sub-type customization
|
# allow sub-type customization
|
||||||
declare = self.declare_paintables
|
declare = self.declare_paintables
|
||||||
|
@ -318,14 +339,10 @@ class Curve(FlowGraphic):
|
||||||
|
|
||||||
p.setPen(self.last_step_pen)
|
p.setPen(self.last_step_pen)
|
||||||
p.drawLine(self._last_line)
|
p.drawLine(self._last_line)
|
||||||
profiler('.drawLine()')
|
profiler('last datum `.drawLine()`')
|
||||||
p.setPen(self._pen)
|
|
||||||
|
|
||||||
|
p.setPen(self._pen)
|
||||||
path = self.path
|
path = self.path
|
||||||
# cap = path.capacity()
|
|
||||||
# if cap != self._last_cap:
|
|
||||||
# print(f'NEW CAPACITY: {self._last_cap} -> {cap}')
|
|
||||||
# self._last_cap = cap
|
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
p.drawPath(path)
|
p.drawPath(path)
|
||||||
|
@ -370,7 +387,7 @@ class Curve(FlowGraphic):
|
||||||
# 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_2last , y[-2],
|
x_2last, y[-2],
|
||||||
x_last, y[-1],
|
x_last, y[-1],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -453,7 +453,8 @@ async def graphics_update_loop(
|
||||||
# and quote_rate >= _quote_throttle_rate * 2
|
# and quote_rate >= _quote_throttle_rate * 2
|
||||||
and quote_rate >= display_rate
|
and quote_rate >= display_rate
|
||||||
):
|
):
|
||||||
log.warning(f'High quote rate {symbol.key}: {quote_rate}')
|
pass
|
||||||
|
# log.warning(f'High quote rate {symbol.key}: {quote_rate}')
|
||||||
|
|
||||||
last_quote_s = time.time()
|
last_quote_s = time.time()
|
||||||
|
|
||||||
|
@ -499,9 +500,9 @@ def graphics_update_cycle(
|
||||||
|
|
||||||
profiler = Profiler(
|
profiler = Profiler(
|
||||||
msg=f'Graphics loop cycle for: `{ds.fqsn}`',
|
msg=f'Graphics loop cycle for: `{ds.fqsn}`',
|
||||||
delayed=True,
|
|
||||||
disabled=not pg_profile_enabled(),
|
disabled=not pg_profile_enabled(),
|
||||||
ms_threshold=ms_slower_then,
|
ms_threshold=ms_slower_then,
|
||||||
|
delayed=True,
|
||||||
# ms_threshold=4,
|
# ms_threshold=4,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1325,7 +1326,7 @@ async def display_symbol_data(
|
||||||
is_ohlc=True,
|
is_ohlc=True,
|
||||||
|
|
||||||
color=bg_chart_color,
|
color=bg_chart_color,
|
||||||
last_bar_color=bg_last_bar_color,
|
last_step_color=bg_last_bar_color,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ensure the last datum graphic is generated
|
# ensure the last datum graphic is generated
|
||||||
|
@ -1362,7 +1363,7 @@ async def display_symbol_data(
|
||||||
is_ohlc=True,
|
is_ohlc=True,
|
||||||
|
|
||||||
color=bg_chart_color,
|
color=bg_chart_color,
|
||||||
last_bar_color=bg_last_bar_color,
|
last_step_color=bg_last_bar_color,
|
||||||
)
|
)
|
||||||
rt_pi.vb.maxmin = partial(
|
rt_pi.vb.maxmin = partial(
|
||||||
rt_chart.maxmin,
|
rt_chart.maxmin,
|
||||||
|
|
|
@ -18,13 +18,8 @@ Super fast OHLC sampling graphics types.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import (
|
|
||||||
Optional,
|
|
||||||
TYPE_CHECKING,
|
|
||||||
)
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
|
||||||
from PyQt5 import (
|
from PyQt5 import (
|
||||||
QtGui,
|
QtGui,
|
||||||
QtWidgets,
|
QtWidgets,
|
||||||
|
@ -33,18 +28,14 @@ from PyQt5.QtCore import (
|
||||||
QLineF,
|
QLineF,
|
||||||
QRectF,
|
QRectF,
|
||||||
)
|
)
|
||||||
|
from PyQt5.QtWidgets import QGraphicsItem
|
||||||
from PyQt5.QtGui import QPainterPath
|
from PyQt5.QtGui import QPainterPath
|
||||||
|
|
||||||
from ._curve import FlowGraphic
|
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 ..log import get_logger
|
from ..log import get_logger
|
||||||
from .._profile import Profiler
|
from .._profile import Profiler
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ._chart import LinkedSplits
|
|
||||||
|
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
@ -100,30 +91,18 @@ class BarItems(FlowGraphic):
|
||||||
"Price range" bars graphics rendered from a OHLC sampled sequence.
|
"Price range" bars graphics rendered from a OHLC sampled sequence.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
# XXX: causes this weird jitter bug when click-drag panning
|
||||||
|
# where the path curve will awkwardly flicker back and forth?
|
||||||
|
cache_mode: int = QGraphicsItem.NoCache
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
linked: LinkedSplits,
|
*args,
|
||||||
plotitem: 'pg.PlotItem', # noqa
|
**kwargs,
|
||||||
color: str = 'bracket',
|
|
||||||
last_bar_color: str = 'original',
|
|
||||||
|
|
||||||
name: Optional[str] = None,
|
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
|
||||||
self.linked = linked
|
|
||||||
# XXX: for the mega-lulz increasing width here increases draw
|
|
||||||
# latency... so probably don't do it until we figure that out.
|
|
||||||
self._color = color
|
|
||||||
self.bars_pen = pg.mkPen(hcolor(color), width=1)
|
|
||||||
self.last_bar_pen = pg.mkPen(hcolor(last_bar_color), width=2)
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
# XXX: causes this weird jitter bug when click-drag panning
|
super().__init__(*args, **kwargs)
|
||||||
# where the path curve will awkwardly flicker back and forth?
|
|
||||||
self.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
|
|
||||||
|
|
||||||
self.path = QPainterPath()
|
|
||||||
self._last_bar_lines: tuple[QLineF, ...] | None = None
|
self._last_bar_lines: tuple[QLineF, ...] | None = None
|
||||||
|
|
||||||
def x_last(self) -> None | float:
|
def x_last(self) -> None | float:
|
||||||
|
@ -218,12 +197,12 @@ class BarItems(FlowGraphic):
|
||||||
# as is necesarry for what's in "view". Not sure if this will
|
# as is necesarry for what's in "view". Not sure if this will
|
||||||
# lead to any perf gains other then when zoomed in to less bars
|
# lead to any perf gains other then when zoomed in to less bars
|
||||||
# in view.
|
# in view.
|
||||||
p.setPen(self.last_bar_pen)
|
p.setPen(self.last_step_pen)
|
||||||
if self._last_bar_lines:
|
if self._last_bar_lines:
|
||||||
p.drawLines(*tuple(filter(bool, self._last_bar_lines)))
|
p.drawLines(*tuple(filter(bool, self._last_bar_lines)))
|
||||||
profiler('draw last bar')
|
profiler('draw last bar')
|
||||||
|
|
||||||
p.setPen(self.bars_pen)
|
p.setPen(self._pen)
|
||||||
p.drawPath(self.path)
|
p.drawPath(self.path)
|
||||||
profiler(f'draw history path: {self.path.capacity()}')
|
profiler(f'draw history path: {self.path.capacity()}')
|
||||||
|
|
||||||
|
@ -299,5 +278,4 @@ class BarItems(FlowGraphic):
|
||||||
# date / from some previous sample. It's weird though
|
# date / from some previous sample. It's weird though
|
||||||
# because i've seen it do this to bars i - 3 back?
|
# because i've seen it do this to bars i - 3 back?
|
||||||
|
|
||||||
# return ohlc['time'], ohlc['close']
|
|
||||||
return ohlc[index_field], ohlc['close']
|
return ohlc[index_field], ohlc['close']
|
||||||
|
|
Loading…
Reference in New Issue