Add an experimental "fast appendable curve" graphic
parent
93e76fa12c
commit
588213a230
|
@ -339,6 +339,133 @@ class LinkedSplitCharts(QtGui.QWidget):
|
|||
return cpw
|
||||
|
||||
|
||||
class FastAppendCurve(pg.PlotCurveItem):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
# TODO: we can probably just dispense with the parent since
|
||||
# we're basically only using the pen setting now...
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._last_line: QtCore.QLineF = None
|
||||
self._xrange: Tuple[int, int] = self.dataBounds(ax=0)
|
||||
|
||||
# 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?
|
||||
self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
||||
|
||||
def update_from_array(
|
||||
self,
|
||||
x,
|
||||
y,
|
||||
) -> QtGui.QPainterPath:
|
||||
|
||||
profiler = pg.debug.Profiler(disabled=True)
|
||||
flip_cache = False
|
||||
|
||||
# print(f"xrange: {self._xrange}")
|
||||
istart, istop = self._xrange
|
||||
|
||||
prepend_length = istart - x[0]
|
||||
append_length = x[-1] - istop
|
||||
|
||||
if self.path is None or prepend_length:
|
||||
self.path = pg.functions.arrayToQPath(
|
||||
x[:-1],
|
||||
y[:-1],
|
||||
connect='all'
|
||||
)
|
||||
profiler('generate fresh path')
|
||||
|
||||
# TODO: get this working - right now it's giving heck on vwap...
|
||||
# if prepend_length:
|
||||
# breakpoint()
|
||||
|
||||
# prepend_path = pg.functions.arrayToQPath(
|
||||
# x[0:prepend_length],
|
||||
# y[0:prepend_length],
|
||||
# connect='all'
|
||||
# )
|
||||
|
||||
# # swap prepend path in "front"
|
||||
# old_path = self.path
|
||||
# self.path = prepend_path
|
||||
# # self.path.moveTo(new_x[0], new_y[0])
|
||||
# self.path.connectPath(old_path)
|
||||
|
||||
if append_length:
|
||||
# print(f"append_length: {append_length}")
|
||||
new_x = x[-append_length - 2:-1]
|
||||
new_y = y[-append_length - 2:-1]
|
||||
# print((new_x, new_y))
|
||||
|
||||
append_path = pg.functions.arrayToQPath(
|
||||
new_x,
|
||||
new_y,
|
||||
connect='all'
|
||||
)
|
||||
# print(f"append_path br: {append_path.boundingRect()}")
|
||||
# self.path.moveTo(new_x[0], new_y[0])
|
||||
# self.path.connectPath(append_path)
|
||||
self.path.connectPath(append_path)
|
||||
|
||||
# XXX: pretty annoying but, without this there's little
|
||||
# artefacts on the append updates to the curve...
|
||||
self.setCacheMode(QtGui.QGraphicsItem.NoCache)
|
||||
self.prepareGeometryChange()
|
||||
flip_cache = True
|
||||
|
||||
# print(f"update br: {self.path.boundingRect()}")
|
||||
|
||||
# XXX: lol brutal, the internals of `CurvePoint` (inherited by
|
||||
# our `LineDot`) required ``.getData()`` to work..
|
||||
self.xData = x
|
||||
self.yData = y
|
||||
|
||||
self._xrange = x[0], x[-1]
|
||||
self._last_line = QtCore.QLineF(x[-2], y[-2], x[-1], y[-1])
|
||||
|
||||
# trigger redraw of path
|
||||
# do update before reverting to cache mode
|
||||
self.prepareGeometryChange()
|
||||
self.update()
|
||||
|
||||
if flip_cache:
|
||||
self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
|
||||
|
||||
def boundingRect(self):
|
||||
hb = self.path.controlPointRect()
|
||||
hb_size = hb.size()
|
||||
# print(f'hb_size: {hb_size}')
|
||||
|
||||
w = hb_size.width() + 1
|
||||
h = hb_size.height() + 1
|
||||
br = QtCore.QRectF(
|
||||
|
||||
# top left
|
||||
QtCore.QPointF(hb.topLeft()),
|
||||
|
||||
# total size
|
||||
QtCore.QSizeF(w, h)
|
||||
)
|
||||
# print(f'bounding rect: {br}')
|
||||
return br
|
||||
|
||||
def paint(self, p, opt, widget):
|
||||
|
||||
profiler = pg.debug.Profiler(disabled=True)
|
||||
# p.setRenderHint(p.Antialiasing, True)
|
||||
|
||||
p.setPen(self.opts['pen'])
|
||||
p.drawLine(self._last_line)
|
||||
profiler('.drawLine()')
|
||||
|
||||
p.drawPath(self.path)
|
||||
profiler('.drawPath()')
|
||||
|
||||
|
||||
class ChartPlotWidget(pg.PlotWidget):
|
||||
"""``GraphicsView`` subtype containing a single ``PlotItem``.
|
||||
|
||||
|
@ -550,8 +677,9 @@ class ChartPlotWidget(pg.PlotWidget):
|
|||
}
|
||||
pdi_kwargs.update(_pdi_defaults)
|
||||
|
||||
# curve = pg.PlotDataItem(
|
||||
# curve = pg.PlotCurveItem(
|
||||
curve = pg.PlotDataItem(
|
||||
curve = FastAppendCurve(
|
||||
y=data[name],
|
||||
x=data['index'],
|
||||
# antialias=True,
|
||||
|
@ -663,6 +791,7 @@ class ChartPlotWidget(pg.PlotWidget):
|
|||
"""Update the named internal graphics from ``array``.
|
||||
|
||||
"""
|
||||
|
||||
if name not in self._overlays:
|
||||
self._ohlc = array
|
||||
else:
|
||||
|
@ -670,11 +799,13 @@ class ChartPlotWidget(pg.PlotWidget):
|
|||
|
||||
curve = self._graphics[name]
|
||||
|
||||
# TODO: we should instead implement a diff based
|
||||
# "only update with new items" on the pg.PlotCurveItem
|
||||
# one place to dig around this might be the `QBackingStore`
|
||||
# https://doc.qt.io/qt-5/qbackingstore.html
|
||||
curve.setData(y=array[name], x=array['index'], **kwargs)
|
||||
if len(array):
|
||||
# TODO: we should instead implement a diff based
|
||||
# "only update with new items" on the pg.PlotCurveItem
|
||||
# one place to dig around this might be the `QBackingStore`
|
||||
# https://doc.qt.io/qt-5/qbackingstore.html
|
||||
# curve.setData(y=array[name], x=array['index'], **kwargs)
|
||||
curve.update_from_array(x=array['index'], y=array[name], **kwargs)
|
||||
|
||||
return curve
|
||||
|
||||
|
@ -1061,6 +1192,7 @@ async def chart_from_quotes(
|
|||
mn_in_view = min(price, mn_in_view)
|
||||
|
||||
if mx_in_view > last_mx or mn_in_view < last_mn:
|
||||
print('scaling')
|
||||
chart._set_yrange(yrange=(mn_in_view, mx_in_view))
|
||||
last_mx, last_mn = mx_in_view, mn_in_view
|
||||
|
||||
|
|
Loading…
Reference in New Issue