Attempt to add numba typing and use `QGraphicsPathItem`
Failed at using either. Quirks in numba's typing require specifying readonly arrays by composing types manually. The graphics item path thing, while it does take less time to write on bar appends, seems to be slower in general in calculating the ``.boundingRect()`` value. Likely we'll just add manual max/min tracking on array updates like ``pg.PlotCurveItem`` to squeeze some final juices on this.to_qpainterpath_and_beyond
parent
949e9d6cd1
commit
3e16840566
|
@ -22,11 +22,13 @@ from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from numba import jit, float64, int64
|
from numba import jit, float64, int64 # , optional
|
||||||
|
# from numba import types as ntypes
|
||||||
from PyQt5 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui
|
||||||
from PyQt5.QtCore import QLineF, QPointF
|
from PyQt5.QtCore import QLineF, QPointF
|
||||||
|
|
||||||
from .._profile import timeit
|
from .._profile import timeit
|
||||||
|
# from ..data._source import numba_ohlc_dtype
|
||||||
from ._style import (
|
from ._style import (
|
||||||
_xaxis_at,
|
_xaxis_at,
|
||||||
hcolor,
|
hcolor,
|
||||||
|
@ -375,18 +377,20 @@ def lines_from_ohlc(row: np.ndarray, w: float) -> Tuple[QLineF]:
|
||||||
|
|
||||||
# @timeit
|
# @timeit
|
||||||
@jit(
|
@jit(
|
||||||
# float64[:](
|
# TODO: for now need to construct this manually for readonly arrays, see
|
||||||
# float64[:],
|
# https://github.com/numba/numba/issues/4511
|
||||||
|
# ntypes.Tuple((float64[:], float64[:], float64[:]))(
|
||||||
|
# numba_ohlc_dtype[::1], # contiguous
|
||||||
|
# int64,
|
||||||
# optional(float64),
|
# optional(float64),
|
||||||
# optional(int64)
|
|
||||||
# ),
|
# ),
|
||||||
nopython=True,
|
nopython=True,
|
||||||
nogil=True
|
nogil=True
|
||||||
)
|
)
|
||||||
def path_arrays_from_ohlc(
|
def path_arrays_from_ohlc(
|
||||||
data: np.ndarray,
|
data: np.ndarray,
|
||||||
w: float64,
|
start: int64,
|
||||||
start: int64 = int64(0),
|
bar_gap: float64 = 0.43,
|
||||||
) -> np.ndarray:
|
) -> np.ndarray:
|
||||||
"""Generate an array of lines objects from input ohlc data.
|
"""Generate an array of lines objects from input ohlc data.
|
||||||
|
|
||||||
|
@ -398,16 +402,7 @@ def path_arrays_from_ohlc(
|
||||||
shape=size,
|
shape=size,
|
||||||
dtype=float64,
|
dtype=float64,
|
||||||
)
|
)
|
||||||
y = np.zeros(
|
y, c = x.copy(), x.copy()
|
||||||
# data,
|
|
||||||
shape=size,
|
|
||||||
dtype=float64,
|
|
||||||
)
|
|
||||||
c = np.zeros(
|
|
||||||
# data,
|
|
||||||
shape=size,
|
|
||||||
dtype=float64,
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: report bug for assert @
|
# TODO: report bug for assert @
|
||||||
# /home/goodboy/repos/piker/env/lib/python3.8/site-packages/numba/core/typing/builtins.py:991
|
# /home/goodboy/repos/piker/env/lib/python3.8/site-packages/numba/core/typing/builtins.py:991
|
||||||
|
@ -426,14 +421,14 @@ def path_arrays_from_ohlc(
|
||||||
istart = i * 6
|
istart = i * 6
|
||||||
istop = istart + 6
|
istop = istart + 6
|
||||||
|
|
||||||
# write points for x, y, and connections
|
# x,y detail the 6 points which connect all vertexes of a ohlc bar
|
||||||
x[istart:istop] = (
|
x[istart:istop] = (
|
||||||
index - w,
|
index - bar_gap,
|
||||||
index,
|
index,
|
||||||
index,
|
index,
|
||||||
index,
|
index,
|
||||||
index,
|
index,
|
||||||
index + w,
|
index + bar_gap,
|
||||||
)
|
)
|
||||||
y[istart:istop] = (
|
y[istart:istop] = (
|
||||||
open,
|
open,
|
||||||
|
@ -443,6 +438,10 @@ def path_arrays_from_ohlc(
|
||||||
close,
|
close,
|
||||||
close,
|
close,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# specifies that the first edge is never connected to the
|
||||||
|
# prior bars last edge thus providing a small "gap"/"space"
|
||||||
|
# between bars determined by ``bar_gap``.
|
||||||
c[istart:istop] = (0, 1, 1, 1, 1, 1)
|
c[istart:istop] = (0, 1, 1, 1, 1, 1)
|
||||||
|
|
||||||
return x, y, c
|
return x, y, c
|
||||||
|
@ -451,11 +450,11 @@ def path_arrays_from_ohlc(
|
||||||
@timeit
|
@timeit
|
||||||
def gen_qpath(
|
def gen_qpath(
|
||||||
data,
|
data,
|
||||||
w,
|
|
||||||
start,
|
start,
|
||||||
|
w,
|
||||||
) -> QtGui.QPainterPath:
|
) -> QtGui.QPainterPath:
|
||||||
|
|
||||||
x, y, c = path_arrays_from_ohlc(data, w, start=start)
|
x, y, c = path_arrays_from_ohlc(data, start, bar_gap=w)
|
||||||
return pg.functions.arrayToQPath(x, y, connect=c)
|
return pg.functions.arrayToQPath(x, y, connect=c)
|
||||||
|
|
||||||
|
|
||||||
|
@ -481,12 +480,15 @@ class BarItems(pg.GraphicsObject):
|
||||||
self.last_bar = QtGui.QPicture()
|
self.last_bar = QtGui.QPicture()
|
||||||
self.history = QtGui.QPicture()
|
self.history = QtGui.QPicture()
|
||||||
|
|
||||||
|
self.path = QtGui.QPainterPath()
|
||||||
|
self._h_path = QtGui.QGraphicsPathItem(self.path)
|
||||||
|
|
||||||
self._pi = plotitem
|
self._pi = plotitem
|
||||||
|
|
||||||
# XXX: not sure this actually needs to be an array other
|
# XXX: not sure this actually needs to be an array other
|
||||||
# then for the old tina mode calcs for up/down bars below?
|
# then for the old tina mode calcs for up/down bars below?
|
||||||
# lines container
|
# lines container
|
||||||
self.lines = _mk_lines_array([], 50e3, 6)
|
# self.lines = _mk_lines_array([], 50e3, 6)
|
||||||
|
|
||||||
# TODO: don't render the full backing array each time
|
# TODO: don't render the full backing array each time
|
||||||
# self._path_data = None
|
# self._path_data = None
|
||||||
|
@ -505,7 +507,7 @@ class BarItems(pg.GraphicsObject):
|
||||||
|
|
||||||
This routine is usually only called to draw the initial history.
|
This routine is usually only called to draw the initial history.
|
||||||
"""
|
"""
|
||||||
self.path = gen_qpath(data, self.w, start=start)
|
self.path = gen_qpath(data, start, self.w)
|
||||||
|
|
||||||
# save graphics for later reference and keep track
|
# save graphics for later reference and keep track
|
||||||
# of current internal "last index"
|
# of current internal "last index"
|
||||||
|
@ -533,7 +535,13 @@ class BarItems(pg.GraphicsObject):
|
||||||
p.drawLines(*tuple(filter(bool, self._last_bar_lines)))
|
p.drawLines(*tuple(filter(bool, self._last_bar_lines)))
|
||||||
p.end()
|
p.end()
|
||||||
|
|
||||||
|
@timeit
|
||||||
def draw_history(self) -> None:
|
def draw_history(self) -> None:
|
||||||
|
# TODO: avoid having to use a ```QPicture` to calc the
|
||||||
|
# ``.boundingRect()``, use ``QGraphicsPathItem`` instead?
|
||||||
|
# https://doc.qt.io/qt-5/qgraphicspathitem.html
|
||||||
|
# self._h_path.setPath(self.path)
|
||||||
|
|
||||||
p = QtGui.QPainter(self.history)
|
p = QtGui.QPainter(self.history)
|
||||||
p.setPen(self.bars_pen)
|
p.setPen(self.bars_pen)
|
||||||
p.drawPath(self.path)
|
p.drawPath(self.path)
|
||||||
|
@ -571,11 +579,10 @@ class BarItems(pg.GraphicsObject):
|
||||||
# generate new graphics to match provided array
|
# generate new graphics to match provided array
|
||||||
# path appending logic:
|
# path appending logic:
|
||||||
# we need to get the previous "current bar(s)" for the time step
|
# we need to get the previous "current bar(s)" for the time step
|
||||||
# and convert it to a path to append to the historical set
|
# and convert it to a sub-path to append to the historical set
|
||||||
new_history_istart = length - 2
|
new_history_istart = length - 2
|
||||||
to_history = array[new_history_istart:new_history_istart + extra]
|
to_history = array[new_history_istart:new_history_istart + extra]
|
||||||
# generate a new sub-path for this now-ready-for-history bar set
|
new_history_qpath = gen_qpath(to_history, 0, self.w)
|
||||||
new_history_qpath = gen_qpath(to_history, self.w, 0)
|
|
||||||
|
|
||||||
# move to position of placement for the next bar in history
|
# move to position of placement for the next bar in history
|
||||||
# and append new sub-path
|
# and append new sub-path
|
||||||
|
@ -646,6 +653,9 @@ class BarItems(pg.GraphicsObject):
|
||||||
p.drawPicture(0, 0, self.last_bar)
|
p.drawPicture(0, 0, self.last_bar)
|
||||||
|
|
||||||
p.setPen(self.bars_pen)
|
p.setPen(self.bars_pen)
|
||||||
|
|
||||||
|
# TODO: does it matter which we use?
|
||||||
|
# p.drawPath(self._h_path.path())
|
||||||
p.drawPath(self.path)
|
p.drawPath(self.path)
|
||||||
|
|
||||||
# @timeit
|
# @timeit
|
||||||
|
@ -664,10 +674,13 @@ class BarItems(pg.GraphicsObject):
|
||||||
# compute aggregate bounding rectangle
|
# compute aggregate bounding rectangle
|
||||||
lb = self.last_bar.boundingRect()
|
lb = self.last_bar.boundingRect()
|
||||||
hb = self.history.boundingRect()
|
hb = self.history.boundingRect()
|
||||||
|
# hb = self._h_path.boundingRect()
|
||||||
|
|
||||||
return QtCore.QRectF(
|
return QtCore.QRectF(
|
||||||
# top left
|
# top left
|
||||||
QtCore.QPointF(hb.topLeft()),
|
QtCore.QPointF(hb.topLeft()),
|
||||||
# total size
|
# total size
|
||||||
|
# QtCore.QSizeF(QtCore.QSizeF(lb.size()) + hb.size())
|
||||||
QtCore.QSizeF(lb.size() + hb.size())
|
QtCore.QSizeF(lb.size() + hb.size())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue