Add updateable y-sticky label

bar_select
Tyler Goodlet 2020-08-30 12:27:41 -04:00
parent 8d29338174
commit d7466a58b4
1 changed files with 91 additions and 36 deletions

View File

@ -3,11 +3,12 @@ Chart axes graphics and behavior.
""" """
import pyqtgraph as pg import pyqtgraph as pg
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QPointF
# from .quantdom.base import Quotes # from .quantdom.base import Quotes
from .quantdom.utils import fromtimestamp from .quantdom.utils import fromtimestamp
from ._style import _font from ._style import _font, hcolor
class PriceAxis(pg.AxisItem): class PriceAxis(pg.AxisItem):
@ -18,7 +19,7 @@ class PriceAxis(pg.AxisItem):
) -> None: ) -> None:
super().__init__(orientation='right') super().__init__(orientation='right')
self.setStyle(**{ self.setStyle(**{
'textFillLimits': [(0, 1)], 'textFillLimits': [(0, 0.5)],
# 'tickTextWidth': 10, # 'tickTextWidth': 10,
# 'tickTextHeight': 25, # 'tickTextHeight': 25,
# 'autoExpandTextSpace': True, # 'autoExpandTextSpace': True,
@ -52,10 +53,11 @@ class DynamicDateAxis(pg.AxisItem):
# default styling # default styling
self.setStyle( self.setStyle(
tickTextOffset=7, tickTextOffset=7,
textFillLimits=[(0, 0.90)], textFillLimits=[(0, 0.70)],
# TODO: doesn't seem to work -> bug in pyqtgraph? # TODO: doesn't seem to work -> bug in pyqtgraph?
# tickTextHeight=2, # tickTextHeight=2,
) )
# self.setHeight(35)
def tickStrings(self, values, scale, spacing): def tickStrings(self, values, scale, spacing):
# if len(values) > 1 or not values: # if len(values) > 1 or not values:
@ -80,13 +82,13 @@ class DynamicDateAxis(pg.AxisItem):
class AxisLabel(pg.GraphicsObject): class AxisLabel(pg.GraphicsObject):
# bg_color = pg.mkColor('#a9a9a9') # bg_color = pg.mkColor('#a9a9a9')
bg_color = pg.mkColor('#808080') bg_color = pg.mkColor(hcolor('gray'))
fg_color = pg.mkColor('#000000') fg_color = pg.mkColor(hcolor('black'))
def __init__( def __init__(
self, self,
parent=None, parent=None,
digits=0, digits=1,
color=None, color=None,
opacity=1, opacity=1,
**kwargs **kwargs
@ -96,33 +98,17 @@ class AxisLabel(pg.GraphicsObject):
self.opacity = opacity self.opacity = opacity
self.label_str = '' self.label_str = ''
self.digits = digits self.digits = digits
# self.quotes_count = len(Quotes) - 1
# some weird color convertion logic?
if isinstance(color, QtGui.QPen): if isinstance(color, QtGui.QPen):
self.bg_color = color.color() self.bg_color = color.color()
self.fg_color = pg.mkColor('#ffffff') self.fg_color = pg.mkColor(hcolor('black'))
elif isinstance(color, list): elif isinstance(color, list):
self.bg_color = {'>0': color[0].color(), '<0': color[1].color()} self.bg_color = {'>0': color[0].color(), '<0': color[1].color()}
self.fg_color = pg.mkColor('#ffffff') self.fg_color = pg.mkColor(hcolor('white'))
self.setFlag(self.ItemIgnoresTransformations) self.setFlag(self.ItemIgnoresTransformations)
def tick_to_string(self, tick_pos):
raise NotImplementedError()
def boundingRect(self): # noqa
raise NotImplementedError()
def update_label(self, evt_post, point_view):
raise NotImplementedError()
def update_label_test(self, ypos=0, ydata=0):
self.label_str = self.tick_to_string(ydata)
height = self.boundingRect().height()
offset = 0 # if have margins
new_pos = QtCore.QPointF(0, ypos - height / 2 - offset)
self.setPos(new_pos)
def paint(self, p, option, widget): def paint(self, p, option, widget):
p.setRenderHint(p.TextAntialiasing, True) p.setRenderHint(p.TextAntialiasing, True)
p.setPen(self.fg_color) p.setPen(self.fg_color)
@ -141,12 +127,38 @@ class AxisLabel(pg.GraphicsObject):
p.drawText(option.rect, self.text_flags, self.label_str) p.drawText(option.rect, self.text_flags, self.label_str)
# uggggghhhh
def tick_to_string(self, tick_pos):
raise NotImplementedError()
def boundingRect(self): # noqa
raise NotImplementedError()
def update_label(self, evt_post, point_view):
raise NotImplementedError()
# end uggggghhhh
# _common_text_flags = (
# QtCore.Qt.TextDontClip |
# QtCore.Qt.AlignCenter |
# QtCore.Qt.AlignTop |
# QtCore.Qt.AlignHCenter |
# QtCore.Qt.AlignVCenter
# )
class XAxisLabel(AxisLabel): class XAxisLabel(AxisLabel):
text_flags = ( text_flags = (
QtCore.Qt.TextDontClip | QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop QtCore.Qt.TextDontClip
| QtCore.Qt.AlignCenter
# | QtCore.Qt.AlignTop
| QtCore.Qt.AlignVCenter
# | QtCore.Qt.AlignHCenter
) )
# text_flags = _common_text_flags
def tick_to_string(self, tick_pos): def tick_to_string(self, tick_pos):
# TODO: change to actual period # TODO: change to actual period
@ -157,34 +169,77 @@ class XAxisLabel(AxisLabel):
return fromtimestamp(bars[round(tick_pos)]['time']).strftime(tpl) return fromtimestamp(bars[round(tick_pos)]['time']).strftime(tpl)
def boundingRect(self): # noqa def boundingRect(self): # noqa
return QtCore.QRectF(0, 0, 145, 50) return QtCore.QRectF(0, 0, 145, 40)
def update_label(self, evt_post, point_view): def update_label(self, abs_pos, data):
ibar = point_view.x() # ibar = view_pos.x()
# if ibar > self.quotes_count: # if ibar > self.quotes_count:
# return # return
self.label_str = self.tick_to_string(ibar) self.label_str = self.tick_to_string(data)
width = self.boundingRect().width() width = self.boundingRect().width()
offset = 0 # if have margins offset = 0 # if have margins
new_pos = QtCore.QPointF(evt_post.x() - width / 2 - offset, 0) new_pos = QPointF(abs_pos.x() - width / 2 - offset, 0)
self.setPos(new_pos) self.setPos(new_pos)
class YAxisLabel(AxisLabel): class YAxisLabel(AxisLabel):
# text_flags = _common_text_flags
text_flags = ( text_flags = (
QtCore.Qt.TextDontClip | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter QtCore.Qt.AlignLeft
| QtCore.Qt.TextDontClip
| QtCore.Qt.AlignVCenter
) )
def tick_to_string(self, tick_pos): def tick_to_string(self, tick_pos):
# WTF IS THIS FORMAT?
return ('{: ,.%df}' % self.digits).format(tick_pos).replace(',', ' ') return ('{: ,.%df}' % self.digits).format(tick_pos).replace(',', ' ')
def boundingRect(self): # noqa def boundingRect(self): # noqa
return QtCore.QRectF(0, 0, 100, 40) return QtCore.QRectF(0, 0, 120, 30)
def update_label(self, evt_post, point_view): def update_label(
self.label_str = self.tick_to_string(point_view.y()) self,
abs_pos: QPointF, # scene coords
data: float, # data for text
) -> None:
self.label_str = self.tick_to_string(data)
height = self.boundingRect().height() height = self.boundingRect().height()
offset = 0 # if have margins offset = 0 # if have margins
new_pos = QtCore.QPointF(0, evt_post.y() - height / 2 - offset) new_pos = QPointF(0, abs_pos.y() - height / 2 - offset)
self.setPos(new_pos) self.setPos(new_pos)
class YSticky(YAxisLabel):
"""Y-axis label that sticks to where it's placed despite chart resizing.
"""
def __init__(
self,
chart,
*args,
**kwargs
) -> None:
super().__init__(*args, **kwargs)
self._chart = chart
# XXX: not sure why this wouldn't work with a proxy?
# pg.SignalProxy(
# delay=0,
# rateLimit=60,
# slot=last.update_on_resize,
# )
chart.sigRangeChanged.connect(self.update_on_resize)
def update_on_resize(self, vr, r):
# TODO: figure out how to generalize across data schema
self.update_from_data(*self._chart._array[-1][['index', 'close']])
def update_from_data(
self,
index: int,
last: float,
) -> None:
self.update_label(
self._chart.mapFromView(QPointF(index, last)),
last
)