Move plotitem overlaying into a `.overlay_plotitem()`
parent
09fc901b0d
commit
e22a652852
|
@ -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:
|
||||||
# Custom viewbox impl
|
raise RuntimeError(
|
||||||
cv = self.mk_vb(name)
|
f'{overlay} must be from `.plotitem_overlay()`'
|
||||||
|
|
||||||
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,),
|
|
||||||
)
|
)
|
||||||
|
pi = overlay
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# this intnernally calls `PlotItem.addItem()` on the
|
|
||||||
# graphics object
|
|
||||||
self.addItem(curve)
|
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue