Fix (really sidestep) flat bar rendering issue(s)

It seems a plethora of problems (including drawing performance) are due
to trying to hack around the strange rendering bug in Qt with `QLineF`
with y1 == y2. There was all sorts of weirdness that would show up with
trying (a hack) to just set all 4 points to the same value including
strange infinite diagonal ghost lines randomly on charts. Instead, just
place hold these flat bar's 'body' line with a `None` and filter the
null values out before calling `QPainter.drawLines()`. This results
in simply no body lines drawn for these datums. We can probably `numba`
the filtering too if it turns out to be a bottleneck.
bar_select
Tyler Goodlet 2020-10-22 20:35:51 -04:00
parent 7be624de39
commit 8c25892521
1 changed files with 30 additions and 38 deletions

View File

@ -13,10 +13,8 @@ from PyQt5.QtCore import QLineF
from ._style import _xaxis_at, hcolor, _font
from ._axes import YAxisLabel, XAxisLabel
# TODO:
# - checkout pyqtgraph.PlotCurveItem.setCompositionMode
_mouse_rate_limit = 60 # calc current screen refresh rate?
_mouse_rate_limit = 40 # calc current screen refresh rate?
_debounce_delay = 1/2e3
_ch_label_opac = 1
@ -48,6 +46,7 @@ class LineDot(pg.CurvePoint):
dot = self.dot = QtGui.QGraphicsEllipseItem(
QtCore.QRectF(-size/2, -size/2, size, size)
)
# if we needed transformable dot?
# dot.translate(-size*0.5, -size*0.5)
dot.setPen(pen)
dot.setBrush(brush)
@ -56,9 +55,6 @@ class LineDot(pg.CurvePoint):
# keep a static size
self.setFlag(self.ItemIgnoresTransformations)
def paint(self, p, opt, widget):
p.drawPicture(0, 0, self._pic)
class CrossHair(pg.GraphicsObject):
@ -234,12 +230,14 @@ def _mk_lines_array(data: List, size: int) -> np.ndarray:
)
# TODO: `numba` this?
def bars_from_ohlc(
data: np.ndarray,
w: float,
start: int = 0,
) -> np.ndarray:
"""Generate an array of lines objects from input ohlc data.
"""
lines = _mk_lines_array(data, data.shape[0])
@ -247,25 +245,22 @@ def bars_from_ohlc(
open, high, low, close, index = q[
['open', 'high', 'low', 'close', 'index']]
# place the x-coord start as "middle" of the drawing range such
# that the open arm line-graphic is at the left-most-side of
# the indexe's range according to the view mapping.
index_start = index
# high - low line
if low != high:
# hl = QLineF(index, low, index, high)
hl = QLineF(index_start, low, index_start, high)
hl = QLineF(index, low, index, high)
else:
# XXX: if we don't do it renders a weird rectangle?
# see below too for handling this later...
hl = QLineF(low, low, low, low)
hl._flat = True
hl = None
# NOTE: place the x-coord start as "middle" of the drawing range such
# that the open arm line-graphic is at the left-most-side of
# the indexe's range according to the view mapping.
# open line
o = QLineF(index_start - w, open, index_start, open)
o = QLineF(index - w, open, index, open)
# close line
c = QLineF(index_start, close, index_start + w, close)
c = QLineF(index, close, index + w, close)
# indexing here is as per the below comments
lines[i] = (hl, o, c)
@ -274,6 +269,7 @@ def bars_from_ohlc(
# array and avoiding the call to `np.ravel()` below?
# lines[3*i:3*i+3] = (hl, o, c)
# XXX: legacy code from candles custom graphics:
# if not _tina_mode:
# else _tina_mode:
# self.lines = lines = np.concatenate(
@ -379,7 +375,11 @@ class BarItems(pg.GraphicsObject):
# use 2d array of lines objects, see conlusion on speed:
# https://stackoverflow.com/a/60089929
to_draw = np.ravel(self.lines[istart:iend])
flat = np.ravel(self.lines[istart:iend])
# TODO: do this with numba for speed gain:
# https://stackoverflow.com/questions/58422690/filtering-a-numpy-array-what-is-the-best-approach
to_draw = flat[np.where(flat != None)] # noqa
# pre-computing a QPicture object allows paint() to run much
# more quickly, rather than re-drawing the shapes every time.
@ -435,35 +435,27 @@ class BarItems(pg.GraphicsObject):
return
# current bar update
i, open, last, = array[-1][['index', 'open', 'close']]
body, larm, rarm = self.lines[index-1]
i, high, low, last, = array[-1][['index', 'high', 'low', 'close']]
assert i == self.index-1
body, larm, rarm = self.lines[i]
# XXX: is there a faster way to modify this?
# update close line / right arm
rarm.setLine(rarm.x1(), last, rarm.x2(), last)
# update body
high = body.y2()
low = body.y1()
if last < low:
low = last
if last > high:
high = last
if getattr(body, '_flat', None) and low != high:
# if the bar was flat it likely does not have
# the index set correctly due to a rendering bug
# see above
body.setLine(i, low, i, high)
body._flat = False
if low != high:
if body is None:
body = self.lines[index-1][0] = QLineF(i, low, i, high)
else:
body.setLine(body.x1(), low, body.x2(), high)
# update body
body.setLine(i, low, i, high)
else:
# XXX: high == low -> remove any HL line to avoid render bug
if body is not None:
body = self.lines[index-1][0] = None
self.draw_lines(just_history=False)
# XXX: From the customGraphicsItem.py example:
# The only required methods are paint() and boundingRect()
# @timeit
def paint(self, p, opt, widget):