Tidy axis code

basic_orders
Tyler Goodlet 2021-02-11 11:48:19 -05:00
parent 02edfdf846
commit dd1aed627e
1 changed files with 63 additions and 31 deletions

View File

@ -16,6 +16,7 @@
"""
Chart axes graphics and behavior.
"""
from typing import List, Tuple, Optional
@ -32,7 +33,7 @@ _axis_pen = pg.mkPen(hcolor('bracket'))
class Axis(pg.AxisItem):
"""A better axis that sizes to typical tick contents considering font size.
"""A better axis that sizes tick contents considering font size.
"""
def __init__(
@ -64,11 +65,17 @@ class Axis(pg.AxisItem):
self.typical_br = _font._qfm.boundingRect(typical_max_str)
# size the pertinent axis dimension to a "typical value"
self.resize()
self.size_to_values()
def size_to_values(self) -> None:
pass
def set_min_tick(self, size: int) -> None:
self._min_tick = size
def txt_offsets(self) -> Tuple[int, int]:
return tuple(self.style['tickTextOffset'])
class PriceAxis(Axis):
@ -77,13 +84,13 @@ class PriceAxis(Axis):
*args,
**kwargs,
) -> None:
super().__init__(*args, orientation='right', **kwargs)
super().__init__(*args, **kwargs)
self.setStyle(**{
# offset of text *away from* axis line in px
'tickTextOffset': 9,
})
def resize(self) -> None:
def size_to_values(self) -> None:
self.setWidth(self.typical_br.width())
# XXX: drop for now since it just eats up h space
@ -116,7 +123,7 @@ class DynamicDateAxis(Axis):
1: '%H:%M:%S',
}
def resize(self) -> None:
def size_to_values(self) -> None:
self.setHeight(self.typical_br.height() + 1)
def _indexes_to_timestrs(
@ -160,22 +167,28 @@ class AxisLabel(pg.GraphicsObject):
def __init__(
self,
parent: Axis,
parent: pg.GraphicsItem,
digits: int = 2,
font_size_inches: Optional[float] = None,
bg_color: str = 'bracket',
fg_color: str = 'black',
opacity: int = 0,
opacity: int = 1, # XXX: seriously don't set this to 0
use_arrow: bool = True,
font_size_inches: Optional[float] = None,
) -> None:
super().__init__(parent)
super().__init__()
self.setParentItem(parent)
self.setFlag(self.ItemIgnoresTransformations)
# XXX: pretty sure this is faster
self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
self.parent = parent
self._parent = parent
self.opacity = opacity
self.label_str = ''
self.digits = digits
@ -200,6 +213,11 @@ class AxisLabel(pg.GraphicsObject):
opt: QtWidgets.QStyleOptionGraphicsItem,
w: QtWidgets.QWidget
) -> None:
"""Draw a filled rectangle based on the size of ``.label_str`` text.
Subtypes can customize further by overloading ``.draw()``.
"""
# p.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
if self.label_str:
@ -207,37 +225,41 @@ class AxisLabel(pg.GraphicsObject):
# if not self.rect:
self._size_br_from_str(self.label_str)
p.setFont(self._dpifont.font)
p.setPen(self.fg_color)
p.setOpacity(self.opacity)
# can be overrided in subtype
self.draw(p, self.rect)
p.setFont(self._dpifont.font)
p.setPen(self.fg_color)
p.drawText(self.rect, self.text_flags, self.label_str)
def draw(
self,
p: QtGui.QPainter,
rect: QtCore.QRectF
) -> None:
# this adds a nice black outline around the label for some odd
# reason; ok by us
# p.setOpacity(self.opacity)
p.drawRect(self.rect)
if self._use_arrow:
if not self.path:
self._draw_arrow_path()
p.drawPath(self.path)
p.fillPath(self.path, pg.mkBrush(self.bg_color))
# this adds a nice black outline around the label for some odd
# reason; ok by us
p.setOpacity(self.opacity)
# this cause the L1 labels to glitch out if used
# in the subtype and it will leave a small black strip
# with the arrow path if done before the above
p.fillRect(self.rect, self.bg_color)
def boundingRect(self): # noqa
"""Size the graphics space from the text contents.
"""
if self.label_str:
self._size_br_from_str(self.label_str)
@ -267,12 +289,14 @@ class AxisLabel(pg.GraphicsObject):
txt_br = self._txt_br = self._dpifont.boundingRect(value)
txt_h, txt_w = txt_br.height(), txt_br.width()
# allow subtypes to specify a static width and height
h, w = self.size_hint()
self.rect = QtCore.QRectF(
0, 0,
(w or txt_w) + self._x_margin/2,
(h or txt_h) + self._y_margin/2,
(w or txt_w) + self._x_margin /2,
(h or txt_h) + self._y_margin /2,
)
# print(self.rect)
# hb = self.path.controlPointRect()
@ -299,7 +323,7 @@ class XAxisLabel(AxisLabel):
def size_hint(self) -> Tuple[float, float]:
# size to parent axis height
return self.parent.height(), None
return self._parent.height(), None
def update_label(
self,
@ -308,7 +332,7 @@ class XAxisLabel(AxisLabel):
offset: int = 0 # if have margins, k?
) -> None:
timestrs = self.parent._indexes_to_timestrs([int(value)])
timestrs = self._parent._indexes_to_timestrs([int(value)])
if not timestrs.any():
return
@ -316,9 +340,10 @@ class XAxisLabel(AxisLabel):
pad = 1*' '
self.label_str = pad + timestrs[0] + pad
y_offset = self.parent.style['tickTextOffset'][1]
_, y_offset = self._parent.txt_offsets()
w = self.boundingRect().width()
self.setPos(QPointF(
abs_pos.x() - w/2,
y_offset/2,
@ -326,13 +351,10 @@ class XAxisLabel(AxisLabel):
self.update()
def _draw_arrow_path(self):
y_offset = self.parent.style['tickTextOffset'][1]
y_offset = self._parent.style['tickTextOffset'][1]
path = QtGui.QPainterPath()
h, w = self.rect.height(), self.rect.width()
# middle = (w + self._y_margin)/2
# middle = (w + self._x_margin)/2
middle = w/2 - 0.5
# aw = (h + self._x_margin)/2
aw = h/2
left = middle - aw
right = middle + aw
@ -341,6 +363,8 @@ class XAxisLabel(AxisLabel):
path.lineTo(right, 0)
path.closeSubpath()
self.path = path
# top left point is local origin and tip of the arrow path
self.tl = QtCore.QPointF(0, -y_offset)
@ -364,15 +388,18 @@ class YAxisLabel(AxisLabel):
super().__init__(*args, **kwargs)
self._chart = chart
chart.sigRangeChanged.connect(self.update_on_resize)
self._last_datum = (None, None)
# pull text offset from axis from parent axis
self.x_offset = self.parent.style['tickTextOffset'][0]
if getattr(self._parent, 'txt_offsets', False):
self.x_offset, y_offset = self._parent.txt_offsets()
def size_hint(self) -> Tuple[float, float]:
# size to parent axis width
return None, self.parent.width()
return None, self._parent.width()
def update_label(
self,
@ -392,6 +419,7 @@ class YAxisLabel(AxisLabel):
br = self.boundingRect()
h = br.height()
self.setPos(QPointF(
x_offset,
abs_pos.y() - h / 2 - self._y_margin / 2
@ -411,6 +439,10 @@ class YAxisLabel(AxisLabel):
index: int,
value: float,
) -> None:
"""Update the label's text contents **and** position from
a view box coordinate datum.
"""
self._last_datum = (index, value)
self.update_label(
self._chart.mapFromView(QPointF(index, value)),
@ -418,7 +450,7 @@ class YAxisLabel(AxisLabel):
)
def _draw_arrow_path(self):
x_offset = self.parent.style['tickTextOffset'][0]
x_offset = self._parent.style['tickTextOffset'][0]
path = QtGui.QPainterPath()
h = self.rect.height()
path.moveTo(0, 0)