Flip contents label stuff into a type

bar_select
Tyler Goodlet 2020-10-29 17:08:03 -04:00
parent aade0e5ea1
commit 416f027c5f
2 changed files with 135 additions and 76 deletions

View File

@ -2,6 +2,7 @@
High level Qt chart widgets. High level Qt chart widgets.
""" """
from typing import Tuple, Dict, Any, Optional from typing import Tuple, Dict, Any, Optional
from functools import partial
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
import numpy as np import numpy as np
@ -15,6 +16,7 @@ from ._axes import (
) )
from ._graphics import ( from ._graphics import (
CrossHair, CrossHair,
ContentsLabel,
BarItems, BarItems,
h_line, h_line,
) )
@ -27,7 +29,6 @@ from ._style import (
_min_points_to_show, _min_points_to_show,
_bars_from_right_in_follow_mode, _bars_from_right_in_follow_mode,
_bars_to_left_in_follow_mode, _bars_to_left_in_follow_mode,
_font,
) )
from ..data._source import Symbol from ..data._source import Symbol
from .. import brokers from .. import brokers
@ -326,7 +327,7 @@ class ChartPlotWidget(pg.PlotWidget):
self.showAxis('right') self.showAxis('right')
# show background grid # show background grid
self.showGrid(x=True, y=True, alpha=0.4) self.showGrid(x=True, y=True, alpha=0.5)
# use cross-hair for cursor? # use cross-hair for cursor?
# self.setCursor(QtCore.Qt.CrossCursor) # self.setCursor(QtCore.Qt.CrossCursor)
@ -344,10 +345,12 @@ class ChartPlotWidget(pg.PlotWidget):
def last_bar_in_view(self) -> bool: def last_bar_in_view(self) -> bool:
self._array[-1]['index'] self._array[-1]['index']
def _update_contents_label(self, index: int) -> None: def update_contents_labels(self, index: int) -> None:
if index >= 0 and index < len(self._array): if index >= 0 and index < len(self._array):
array = self._array
for name, (label, update) in self._labels.items(): for name, (label, update) in self._labels.items():
update(index) update(index, array)
def _set_xlimits( def _set_xlimits(
self, self,
@ -375,54 +378,6 @@ class ChartPlotWidget(pg.PlotWidget):
rbar = min(r, len(self._array)) rbar = min(r, len(self._array))
return l, lbar, rbar, r return l, lbar, rbar, r
def draw_ohlc(
self,
name: str,
data: np.ndarray,
# XXX: pretty sure this is dumb and we don't need an Enum
style: pg.GraphicsObject = BarItems,
) -> pg.GraphicsObject:
"""Draw OHLC datums to chart.
"""
graphics = style(self.plotItem)
# adds all bar/candle graphics objects for each data point in
# the np array buffer to be drawn on next render cycle
self.addItem(graphics)
# draw after to allow self.scene() to work...
graphics.draw_from_data(data)
self._graphics[name] = graphics
# XXX: How to stack labels vertically?
# Ogi says: "use ..."
label = pg.LabelItem(
justify='left',
size=f'{_font.pixelSize()}px',
)
label.setParentItem(self._vb)
self.scene().addItem(label)
# keep close to top
label.anchor(itemPos=(0, 0), parentPos=(0, 0), offset=(0, -4))
def update(index: int) -> None:
label.setText(
"{name}[{index}] -> O:{} H:{} L:{} C:{} V:{}".format(
*self._array[index].item()[2:8],
name=name,
index=index,
)
)
self._labels[name] = (label, update)
self._update_contents_label(len(data) - 1)
label.show()
self._add_sticky(name)
return graphics
def default_view( def default_view(
self, self,
index: int = -1, index: int = -1,
@ -456,6 +411,34 @@ class ChartPlotWidget(pg.PlotWidget):
padding=0, padding=0,
) )
def draw_ohlc(
self,
name: str,
data: np.ndarray,
# XXX: pretty sure this is dumb and we don't need an Enum
style: pg.GraphicsObject = BarItems,
) -> pg.GraphicsObject:
"""Draw OHLC datums to chart.
"""
graphics = style(self.plotItem)
# adds all bar/candle graphics objects for each data point in
# the np array buffer to be drawn on next render cycle
self.addItem(graphics)
# draw after to allow self.scene() to work...
graphics.draw_from_data(data)
self._graphics[name] = graphics
label = ContentsLabel(chart=self, anchor_at=('top', 'left'))
self._labels[name] = (label, partial(label.update_from_ohlc, name))
label.show()
self.update_contents_labels(len(data) - 1)
self._add_sticky(name)
return graphics
def draw_curve( def draw_curve(
self, self,
name: str, name: str,
@ -482,37 +465,21 @@ class ChartPlotWidget(pg.PlotWidget):
# register overlay curve with name # register overlay curve with name
self._graphics[name] = curve self._graphics[name] = curve
# XXX: How to stack labels vertically?
label = pg.LabelItem(
justify='left',
size=f'{_font.pixelSize()}px',
)
# anchor to the viewbox
label.setParentItem(self._vb)
# label.setParentItem(self.getPlotItem())
if overlay: if overlay:
# position bottom left if an overlay anchor_at = ('bottom', 'right')
label.anchor(itemPos=(1, 1), parentPos=(1, 1), offset=(0, 3))
self._overlays[name] = curve self._overlays[name] = curve
else: else:
label.anchor(itemPos=(0, 0), parentPos=(0, 0), offset=(0, -4)) anchor_at = ('top', 'right')
# 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) self._add_sticky(name)
def update(index: int) -> None: label = ContentsLabel(chart=self, anchor_at=anchor_at)
data = self._array[index][name] self._labels[name] = (label, partial(label.update_from_value, name))
label.setText(f"{name}: {data:.2f}")
label.show() label.show()
self.scene().addItem(label) self.update_contents_labels(len(data) - 1)
self._labels[name] = (label, update)
self._update_contents_label(len(data) - 1)
if self._cursor: if self._cursor:
self._cursor.add_curve_cursor(self, curve) self._cursor.add_curve_cursor(self, curve)
@ -909,7 +876,7 @@ async def chart_from_fsp(
) )
# display contents labels asap # display contents labels asap
chart._update_contents_label(len(shm.array) - 1) chart.update_contents_labels(len(shm.array) - 1)
array = shm.array array = shm.array
value = array[fsp_func_name][-1] value = array[fsp_func_name][-1]

View File

@ -6,10 +6,11 @@ from typing import List
import numpy as np import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
# from numba import jit, float64, optional, int64
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QLineF, QPointF from PyQt5.QtCore import QLineF, QPointF
# from .quantdom.utils import timeit # from .._profile import timeit
from ._style import _xaxis_at, hcolor, _font from ._style import _xaxis_at, hcolor, _font
from ._axes import YAxisLabel, XAxisLabel from ._axes import YAxisLabel, XAxisLabel
@ -56,6 +57,78 @@ class LineDot(pg.CurvePoint):
self.setFlag(self.ItemIgnoresTransformations) self.setFlag(self.ItemIgnoresTransformations)
_corner_anchors = {
'top': 0,
'left': 0,
'bottom': 1,
'right': 1,
}
# XXX: fyi naming here is confusing / opposite to coords
_corner_margins = {
('top', 'left'): (-4, -5),
('top', 'right'): (4, -5),
('bottom', 'left'): (-4, 5),
('bottom', 'right'): (4, 5),
}
class ContentsLabel(pg.LabelItem):
"""Label anchored to a ``ViewBox`` typically for displaying
datum-wise points from the "viewed" contents.
"""
def __init__(
self,
chart: 'ChartPlotWidget', # noqa
anchor_at: str = ('top', 'right'),
justify_text: str = 'left',
size: str = f'{_font.pixelSize()}px',
) -> None:
super().__init__(justify=justify_text, size=size)
# anchor to viewbox
self.setParentItem(chart._vb)
chart.scene().addItem(self)
self.chart = chart
v, h = anchor_at
index = (_corner_anchors[h], _corner_anchors[v])
margins = _corner_margins[(v, h)]
self.anchor(itemPos=index, parentPos=index, offset=margins)
def update_from_ohlc(
self,
name: str,
index: int,
array: np.ndarray,
) -> None:
# this being "html" is the dumbest shit :eyeroll:
self.setText(
"<b>i</b>:{index}<br/>"
"<b>O</b>:{}<br/>"
"<b>H</b>:{}<br/>"
"<b>L</b>:{}<br/>"
"<b>C</b>:{}<br/>"
"<b>V</b>:{}".format(
# *self._array[index].item()[2:8],
*array[index].item()[2:8],
name=name,
index=index,
)
)
def update_from_value(
self,
name: str,
index: int,
array: np.ndarray,
) -> None:
data = array[index][name]
self.setText(f"{name}: {data:.2f}")
class CrossHair(pg.GraphicsObject): class CrossHair(pg.GraphicsObject):
def __init__( def __init__(
@ -208,7 +281,7 @@ class CrossHair(pg.GraphicsObject):
opts['vl'].setX(ix) opts['vl'].setX(ix)
# update the chart's "contents" label # update the chart's "contents" label
plot._update_contents_label(ix) plot.update_contents_labels(ix)
# update all subscribed curve dots # update all subscribed curve dots
for cursor in opts.get('cursors', ()): for cursor in opts.get('cursors', ()):
@ -235,6 +308,15 @@ class CrossHair(pg.GraphicsObject):
return self.plots[0].boundingRect() return self.plots[0].boundingRect()
# @jit(
# # float64[:](
# # float64[:],
# # optional(float64),
# # optional(int16)
# # ),
# nopython=True,
# nogil=True
# )
def _mk_lines_array(data: List, size: int) -> np.ndarray: def _mk_lines_array(data: List, size: int) -> np.ndarray:
"""Create an ndarray to hold lines graphics objects. """Create an ndarray to hold lines graphics objects.
""" """
@ -246,6 +328,16 @@ def _mk_lines_array(data: List, size: int) -> np.ndarray:
# TODO: `numba` this? # TODO: `numba` this?
# @jit(
# # float64[:](
# # float64[:],
# # optional(float64),
# # optional(int16)
# # ),
# nopython=True,
# nogil=True
# )
def bars_from_ohlc( def bars_from_ohlc(
data: np.ndarray, data: np.ndarray,
w: float, w: float,