Use filled rect for current step
A `QRectF` is easier to make and draw (i think?) so use that and fill it on volume events for decent sleek real-time look. Adjust the step array generator to allow for an endpoints flag. Comment and/or clean out all the old path filling calls that gave us perf issues..windows_testing_volume
parent
6dd42bcfb3
commit
940fc5d2f0
|
@ -18,7 +18,7 @@
|
||||||
Fast, smooth, sexy curves.
|
Fast, smooth, sexy curves.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Tuple
|
from typing import Optional
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
|
@ -37,6 +37,7 @@ from ._style import hcolor
|
||||||
def step_path_arrays_from_1d(
|
def step_path_arrays_from_1d(
|
||||||
x: np.ndarray,
|
x: np.ndarray,
|
||||||
y: np.ndarray,
|
y: np.ndarray,
|
||||||
|
include_endpoints: bool = False,
|
||||||
|
|
||||||
) -> (np.ndarray, np.ndarray):
|
) -> (np.ndarray, np.ndarray):
|
||||||
'''Generate a "step mode" curve aligned with OHLC style bars
|
'''Generate a "step mode" curve aligned with OHLC style bars
|
||||||
|
@ -63,46 +64,24 @@ def step_path_arrays_from_1d(
|
||||||
# we create a 1d with 2 extra indexes to
|
# we create a 1d with 2 extra indexes to
|
||||||
# hold the start and (current) end value for the steps
|
# hold the start and (current) end value for the steps
|
||||||
# on either end
|
# on either end
|
||||||
|
y2 = np.empty((len(y), 2), dtype=y.dtype)
|
||||||
|
y2[:] = y[:, np.newaxis]
|
||||||
|
|
||||||
y_out = np.empty(
|
y_out = np.empty(
|
||||||
2*len(y) + 2,
|
2*len(y) + 2,
|
||||||
dtype=y.dtype
|
dtype=y.dtype
|
||||||
)
|
)
|
||||||
y2 = np.empty((len(y), 2), dtype=y.dtype)
|
|
||||||
y2[:] = y[:, np.newaxis]
|
|
||||||
|
|
||||||
# flatten and set 0 endpoints
|
# flatten and set 0 endpoints
|
||||||
y_out[1:-1] = y2.reshape(y2.size)
|
y_out[1:-1] = y2.reshape(y2.size)
|
||||||
y_out[0] = 0
|
y_out[0] = 0
|
||||||
y_out[-1] = 0
|
y_out[-1] = 0
|
||||||
|
|
||||||
return x_out, y_out
|
if not include_endpoints:
|
||||||
|
return x_out[:-1], y_out[:-1]
|
||||||
|
|
||||||
|
else:
|
||||||
def step_lines_from_point(
|
return x_out, y_out
|
||||||
index: float,
|
|
||||||
level: float,
|
|
||||||
|
|
||||||
) -> Tuple[QLineF]:
|
|
||||||
|
|
||||||
# TODO: maybe consider using `QGraphicsLineItem` ??
|
|
||||||
# gives us a ``.boundingRect()`` on the objects which may make
|
|
||||||
# computing the composite bounding rect of the last bars + the
|
|
||||||
# history path faster since it's done in C++:
|
|
||||||
# https://doc.qt.io/qt-5/qgraphicslineitem.html
|
|
||||||
|
|
||||||
# index = x[0]
|
|
||||||
# level = y[0]
|
|
||||||
|
|
||||||
# (x0 - 0.5, 0) -> (x0 - 0.5, y0)
|
|
||||||
left = QLineF(index - 0.5, 0, index - 0.5, level)
|
|
||||||
|
|
||||||
# (x0 - 0.5, y0) -> (x1 + 0.5, y1)
|
|
||||||
top = QLineF(index - 0.5, level, index + 0.5, level)
|
|
||||||
|
|
||||||
# (x1 + 0.5, y1 -> (x1 + 0.5, 0)
|
|
||||||
right = QLineF(index + 0.5, level, index + 0.5, 0)
|
|
||||||
|
|
||||||
return [left, top, right]
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: got a feeling that dropping this inheritance gets us even more speedups
|
# TODO: got a feeling that dropping this inheritance gets us even more speedups
|
||||||
|
@ -112,7 +91,11 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
step_mode: bool = False,
|
step_mode: bool = False,
|
||||||
|
color: str = 'default_lightest',
|
||||||
|
fill_color: Optional[str] = None,
|
||||||
|
|
||||||
**kwargs
|
**kwargs
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
# TODO: we can probably just dispense with the parent since
|
# TODO: we can probably just dispense with the parent since
|
||||||
|
@ -120,13 +103,12 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self._last_line: QLineF = None
|
self._last_line: QLineF = None
|
||||||
self._xrange: Tuple[int, int] = self.dataBounds(ax=0)
|
self._xrange: tuple[int, int] = self.dataBounds(ax=0)
|
||||||
self._step_mode: bool = step_mode
|
self._step_mode: bool = step_mode
|
||||||
self._fill = False
|
self._fill = False
|
||||||
|
|
||||||
color = hcolor('davies')
|
self.setBrush(hcolor(fill_color or color))
|
||||||
self.setBrush(color)
|
self.setPen(hcolor(color))
|
||||||
self.setPen(color)
|
|
||||||
|
|
||||||
# TODO: one question still remaining is if this makes trasform
|
# TODO: one question still remaining is if this makes trasform
|
||||||
# interactions slower (such as zooming) and if so maybe if/when
|
# interactions slower (such as zooming) and if so maybe if/when
|
||||||
|
@ -168,8 +150,8 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
)
|
)
|
||||||
profiler('generate fresh path')
|
profiler('generate fresh path')
|
||||||
|
|
||||||
if self._step_mode:
|
# if self._step_mode:
|
||||||
self.path.closeSubpath()
|
# self.path.closeSubpath()
|
||||||
|
|
||||||
# TODO: get this working - right now it's giving heck on vwap...
|
# TODO: get this working - right now it's giving heck on vwap...
|
||||||
# if prepend_length:
|
# if prepend_length:
|
||||||
|
@ -187,13 +169,14 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
# # self.path.moveTo(new_x[0], new_y[0])
|
# # self.path.moveTo(new_x[0], new_y[0])
|
||||||
# self.path.connectPath(old_path)
|
# self.path.connectPath(old_path)
|
||||||
|
|
||||||
if append_length:
|
elif append_length:
|
||||||
if self._step_mode:
|
if self._step_mode:
|
||||||
new_x = x[-append_length - 2:-1]
|
new_x, new_y = step_path_arrays_from_1d(
|
||||||
new_y = y[-append_length - 2:-1]
|
x[-append_length - 2:-1],
|
||||||
new_x, new_y = step_path_arrays_from_1d(new_x, new_y)
|
y[-append_length - 2:-1],
|
||||||
# new_x = new_x[3:]
|
)
|
||||||
# new_y = new_y[3:]
|
new_x = new_x[1:]
|
||||||
|
new_y = new_y[1:]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# print(f"append_length: {append_length}")
|
# print(f"append_length: {append_length}")
|
||||||
|
@ -205,28 +188,26 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
new_x,
|
new_x,
|
||||||
new_y,
|
new_y,
|
||||||
connect='all',
|
connect='all',
|
||||||
finiteCheck=False,
|
# finiteCheck=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
path = self.path
|
||||||
if self._step_mode:
|
if self._step_mode:
|
||||||
|
|
||||||
if self._fill:
|
if self._fill:
|
||||||
path = self.path
|
# XXX: super slow set "union" op
|
||||||
# self.path = self.path.united(append_path).simplified()
|
self.path = self.path.united(append_path).simplified()
|
||||||
path.addPath(append_path.simplified())
|
|
||||||
# path.connectPath(append_path.simplified())
|
# path.addPath(append_path)
|
||||||
path.closeSubpath()
|
# path.closeSubpath()
|
||||||
# path.simplified()
|
|
||||||
else:
|
else:
|
||||||
self.path.connectPath(append_path.simplified())
|
# path.addPath(append_path)
|
||||||
|
self.path.connectPath(append_path)
|
||||||
else:
|
else:
|
||||||
# print(f"append_path br: {append_path.boundingRect()}")
|
# print(f"append_path br: {append_path.boundingRect()}")
|
||||||
# self.path.moveTo(new_x[0], new_y[0])
|
# self.path.moveTo(new_x[0], new_y[0])
|
||||||
# self.path.connectPath(append_path)
|
# self.path.connectPath(append_path)
|
||||||
self.path.connectPath(append_path.simplified())
|
path.connectPath(append_path)
|
||||||
|
|
||||||
# if self._step_mode:
|
|
||||||
# self.path.closeSubpath()
|
|
||||||
# self.fill_path.connectPath(
|
|
||||||
|
|
||||||
# XXX: pretty annoying but, without this there's little
|
# XXX: pretty annoying but, without this there's little
|
||||||
# artefacts on the append updates to the curve...
|
# artefacts on the append updates to the curve...
|
||||||
|
@ -243,8 +224,7 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
|
|
||||||
self._xrange = x[0], x[-1]
|
self._xrange = x[0], x[-1]
|
||||||
if self._step_mode:
|
if self._step_mode:
|
||||||
# TODO: use a ``QRectF`` and ``QPainterPath.addRect()``
|
self._last_step_rect = QRectF(x[-1] - 0.5, 0, x[-1] + 0.5, y[-1])
|
||||||
self._last_step_lines = step_lines_from_point(x[-1], y[-1])
|
|
||||||
else:
|
else:
|
||||||
self._last_line = QLineF(x[-2], y[-2], x[-1], y[-1])
|
self._last_line = QLineF(x[-2], y[-2], x[-1], y[-1])
|
||||||
|
|
||||||
|
@ -301,16 +281,16 @@ class FastAppendCurve(pg.PlotCurveItem):
|
||||||
|
|
||||||
if self._step_mode:
|
if self._step_mode:
|
||||||
|
|
||||||
p.drawLines(*tuple(filter(bool, self._last_step_lines)))
|
brush = self.opts['brush']
|
||||||
|
# p.drawLines(*tuple(filter(bool, self._last_step_lines)))
|
||||||
|
# p.drawRect(self._last_step_rect)
|
||||||
|
p.fillRect(self._last_step_rect, brush)
|
||||||
|
|
||||||
p.drawPath(self.path)
|
p.drawPath(self.path)
|
||||||
|
|
||||||
# fill_path = QtGui.QPainterPath(self.path)
|
|
||||||
# if self._fill:
|
|
||||||
# self.path.closeSubpath()
|
|
||||||
if self._fill:
|
if self._fill:
|
||||||
print('FILLED')
|
print('FILLED')
|
||||||
p.fillPath(self.path, self.opts['brush'])
|
p.fillPath(self.path, brush)
|
||||||
|
|
||||||
profiler('.drawPath()')
|
profiler('.drawPath()')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue