Move plotitem overlaying into a `.overlay_plotitem()`

vlm_plotz
Tyler Goodlet 2022-01-12 18:29:07 -05:00
parent d0693e2967
commit ca9973e619
1 changed files with 79 additions and 89 deletions

View File

@ -19,8 +19,6 @@ High level chart-widget apis.
''' '''
from __future__ import annotations from __future__ import annotations
from functools import partial
from dataclasses import dataclass
from typing import Optional from typing import Optional
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
@ -383,8 +381,9 @@ class LinkedSplits(QWidget):
style: str = 'bar', style: str = 'bar',
) -> 'ChartPlotWidget': ) -> ChartPlotWidget:
'''Start up and show main (price) chart and all linked subcharts. '''
Start up and show main (price) chart and all linked subcharts.
The data input struct array must include OHLC fields. The data input struct array must include OHLC fields.
@ -537,7 +536,7 @@ class LinkedSplits(QWidget):
) )
self.cursor.contents_labels.add_label( self.cursor.contents_labels.add_label(
cpw, cpw,
'ohlc', name,
anchor_at=('top', 'left'), anchor_at=('top', 'left'),
update_func=ContentsLabel.update_from_ohlc, update_func=ContentsLabel.update_from_ohlc,
) )
@ -611,11 +610,11 @@ class LinkedSplits(QWidget):
# import pydantic # import pydantic
# class ArrayScene(pydantic.BaseModel): # class Graphics(pydantic.BaseModel):
# ''' # '''
# Data-AGGRegate: high level API onto multiple (categorized) # Data-AGGRegate: high level API onto multiple (categorized)
# ``ShmArray``s with high level processing routines mostly for # ``ShmArray``s with high level processing routines for
# graphics summary and display. # graphics computations and display.
# ''' # '''
# arrays: dict[str, np.ndarray] = {} # arrays: dict[str, np.ndarray] = {}
@ -738,7 +737,7 @@ class ChartPlotWidget(pg.PlotWidget):
self.default_view() self.default_view()
self.cv.enable_auto_yrange() self.cv.enable_auto_yrange()
self.overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem) self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
def resume_all_feeds(self): def resume_all_feeds(self):
for feed in self._feeds.values(): for feed in self._feeds.values():
@ -849,7 +848,7 @@ class ChartPlotWidget(pg.PlotWidget):
# adds all bar/candle graphics objects for each data point in # adds all bar/candle graphics objects for each data point in
# the np array buffer to be drawn on next render cycle # the np array buffer to be drawn on next render cycle
self.addItem(graphics) self.plotItem.addItem(graphics)
# draw after to allow self.scene() to work... # draw after to allow self.scene() to work...
graphics.draw_from_data(data) graphics.draw_from_data(data)
@ -860,6 +859,57 @@ class ChartPlotWidget(pg.PlotWidget):
return graphics, data_key return graphics, data_key
def overlay_plotitem(
self,
name: str,
) -> pg.PlotItem:
# Custom viewbox impl
cv = self.mk_vb(name)
cv.chart = self
# xaxis = DynamicDateAxis(
# orientation='bottom',
# linkedsplits=self.linked,
# )
yaxis = PriceAxis(
orientation='right',
linkedsplits=self.linked,
)
plotitem = pg.PlotItem(
parent=self.plotItem,
name=name,
enableMenu=False,
viewBox=cv,
axisItems={
# 'bottom': xaxis,
'right': yaxis,
},
default_axes=[],
)
# plotitem.setAxisItems(
# add_to_layout=False,
# axisItems={
# 'bottom': xaxis,
# 'right': yaxis,
# },
# )
# plotite.hideAxis('right')
# plotite.hideAxis('bottom')
# plotitem.addItem(curve)
cv.enable_auto_yrange()
# plotitem.enableAutoRange(axis='y')
plotitem.hideButtons()
self.pi_overlay.add_plotitem(
plotitem,
# only link x-axes,
link_axes=(0,),
)
return plotitem
def draw_curve( def draw_curve(
self, self,
@ -868,7 +918,6 @@ class ChartPlotWidget(pg.PlotWidget):
array_key: Optional[str] = None, array_key: Optional[str] = None,
overlay: bool = False, overlay: bool = False,
separate_axes: bool = False,
color: Optional[str] = None, color: Optional[str] = None,
add_label: bool = True, add_label: bool = True,
@ -907,11 +956,11 @@ class ChartPlotWidget(pg.PlotWidget):
**pdi_kwargs, **pdi_kwargs,
) )
# XXX: see explanation for differenct caching modes: # XXX: see explanation for different caching modes:
# https://stackoverflow.com/a/39410081 # https://stackoverflow.com/a/39410081
# seems to only be useful if we don't re-generate the entire # seems to only be useful if we don't re-generate the entire
# QPainterPath every time # QPainterPath every time
# curve.curve.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache) # curve.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
# don't ever use this - it's a colossal nightmare of artefacts # don't ever use this - it's a colossal nightmare of artefacts
# and is disastrous for performance. # and is disastrous for performance.
@ -921,83 +970,28 @@ class ChartPlotWidget(pg.PlotWidget):
self._graphics[name] = curve self._graphics[name] = curve
self._arrays[data_key] = data self._arrays[data_key] = data
pi = self.plotItem
# TODO: this probably needs its own method?
if overlay: if overlay:
# anchor_at = ('bottom', 'left') # anchor_at = ('bottom', 'left')
self._overlays[name] = None self._overlays[name] = None
if separate_axes: if isinstance(overlay, pg.PlotItem):
if overlay not in self.pi_overlay.overlays:
raise RuntimeError(
f'{overlay} must be from `.plotitem_overlay()`'
)
pi = overlay
# Custom viewbox impl
cv = self.mk_vb(name)
def maxmin():
return self.maxmin(name=data_key)
# ensure view maxmin is computed from correct array
# cv._maxmin = partial(self.maxmin, name=data_key)
cv._maxmin = maxmin
cv.chart = self
# xaxis = DynamicDateAxis(
# orientation='bottom',
# linkedsplits=self.linked,
# )
yaxis = PriceAxis(
orientation='right',
linkedsplits=self.linked,
)
plotitem = pg.PlotItem(
parent=self.plotItem,
name=name,
enableMenu=False,
viewBox=cv,
axisItems={
# 'bottom': xaxis,
'right': yaxis,
},
default_axes=[],
)
# plotitem.setAxisItems(
# add_to_layout=False,
# axisItems={
# 'bottom': xaxis,
# 'right': yaxis,
# },
# )
# plotite.hideAxis('right')
# plotite.hideAxis('bottom')
plotitem.addItem(curve)
cv.enable_auto_yrange()
# config
# plotitem.setAutoVisible(y=True)
# plotitem.enableAutoRange(axis='y')
plotitem.hideButtons()
self.overlay.add_plotitem(
plotitem,
# only link x-axes,
link_axes=(0,),
)
else:
# this intnernally calls `PlotItem.addItem()` on the
# graphics object
self.addItem(curve)
else: else:
# this intnernally calls `PlotItem.addItem()` on the
# graphics object
self.addItem(curve)
# anchor_at = ('top', 'left') # anchor_at = ('top', 'left')
# TODO: something instead of stickies for overlays # TODO: something instead of stickies for overlays
# (we need something that avoids clutter on x-axis). # (we need something that avoids clutter on x-axis).
self._add_sticky(name, bg_color=color) self._add_sticky(name, bg_color=color)
pi.addItem(curve)
return curve, data_key return curve, data_key
# TODO: make this a ctx mngr # TODO: make this a ctx mngr
@ -1036,7 +1030,8 @@ class ChartPlotWidget(pg.PlotWidget):
**kwargs, **kwargs,
) -> pg.GraphicsObject: ) -> pg.GraphicsObject:
'''Update the named internal graphics from ``array``. '''
Update the named internal graphics from ``array``.
''' '''
self._arrays[self.name] = array self._arrays[self.name] = array
@ -1053,7 +1048,8 @@ class ChartPlotWidget(pg.PlotWidget):
**kwargs, **kwargs,
) -> pg.GraphicsObject: ) -> pg.GraphicsObject:
'''Update the named internal graphics from ``array``. '''
Update the named internal graphics from ``array``.
''' '''
assert len(array) assert len(array)
@ -1123,7 +1119,7 @@ class ChartPlotWidget(pg.PlotWidget):
def get_index(self, time: float) -> int: def get_index(self, time: float) -> int:
# TODO: this should go onto some sort of # TODO: this should go onto some sort of
# data-view strimg thinger..right? # data-view thinger..right?
ohlc = self._shm.array ohlc = self._shm.array
# XXX: not sure why the time is so off here # XXX: not sure why the time is so off here
@ -1178,15 +1174,9 @@ class ChartPlotWidget(pg.PlotWidget):
yhigh = np.nanmax(bars['high']) yhigh = np.nanmax(bars['high'])
else: else:
try: view = bars[name or self.data_key]
view = bars[name or self.data_key]
except:
breakpoint()
# if self.data_key != 'volume':
# else:
# view = bars
ylow = np.nanmin(view) ylow = np.nanmin(view)
yhigh = np.nanmax(view) yhigh = np.nanmax(view)
# print(f'{(ylow, yhigh)}')
# print(f'{(ylow, yhigh)}')
return ylow, yhigh return ylow, yhigh