Tidy axis code
parent
02edfdf846
commit
dd1aed627e
|
@ -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,6 +289,8 @@ 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(
|
||||
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue