Rework axes types, sizing stuff
Make our own ``Axis`` and have it call an impl specific ``.resize()`` such that different axes can size to their own spec. Allow passing in a "typical maximum value string" which will be used by default for sizing the axis' minor dimension; a common value should be passed to all axes in a linked split charts widget. Add size hinting for axes labels such that they can check their parent (axis) for desired dimensions if needed.bar_select
parent
89d48afb6c
commit
23672fc22b
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Chart axes graphics and behavior.
|
Chart axes graphics and behavior.
|
||||||
"""
|
"""
|
||||||
from typing import List
|
from typing import List, Tuple
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
|
@ -14,12 +14,19 @@ from ..data._source import float_digits
|
||||||
_axis_pen = pg.mkPen(hcolor('bracket'))
|
_axis_pen = pg.mkPen(hcolor('bracket'))
|
||||||
|
|
||||||
|
|
||||||
class PriceAxis(pg.AxisItem):
|
class Axis(pg.AxisItem):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
linked_charts,
|
||||||
|
typical_max_str: str = '100 000.00',
|
||||||
|
**kwargs
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(orientation='right')
|
|
||||||
|
self.linked_charts = linked_charts
|
||||||
|
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.setTickFont(_font)
|
self.setTickFont(_font)
|
||||||
self.setStyle(**{
|
self.setStyle(**{
|
||||||
'textFillLimits': [(0, 0.666)],
|
'textFillLimits': [(0, 0.666)],
|
||||||
|
@ -29,13 +36,31 @@ class PriceAxis(pg.AxisItem):
|
||||||
# 'tickTextWidth': 40,
|
# 'tickTextWidth': 40,
|
||||||
# 'autoExpandTextSpace': True,
|
# 'autoExpandTextSpace': True,
|
||||||
# 'maxTickLength': -20,
|
# 'maxTickLength': -20,
|
||||||
# 'stopAxisAtTick': (True, True), # doesn't work well on price
|
|
||||||
|
# doesn't work well on price?
|
||||||
|
# 'stopAxisAtTick': (True, True),
|
||||||
})
|
})
|
||||||
# self.setLabel(**{'font-size': '10pt'})
|
# self.setLabel(**{'font-size': '10pt'})
|
||||||
|
|
||||||
self.setTickFont(_font)
|
self.setTickFont(_font)
|
||||||
self.setPen(_axis_pen)
|
self.setPen(_axis_pen)
|
||||||
|
self.typical_br = _font._fm.boundingRect(typical_max_str)
|
||||||
|
|
||||||
self.setWidth(40)
|
# size the pertinent axis dimension to a "typical value"
|
||||||
|
self.resize()
|
||||||
|
|
||||||
|
|
||||||
|
class PriceAxis(Axis):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(*args, orientation='right', **kwargs)
|
||||||
|
|
||||||
|
def resize(self) -> None:
|
||||||
|
self.setWidth(self.typical_br.width())
|
||||||
|
|
||||||
# XXX: drop for now since it just eats up h space
|
# XXX: drop for now since it just eats up h space
|
||||||
|
|
||||||
|
@ -50,7 +75,8 @@ class PriceAxis(pg.AxisItem):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DynamicDateAxis(pg.AxisItem):
|
class DynamicDateAxis(Axis):
|
||||||
|
|
||||||
# time formats mapped by seconds between bars
|
# time formats mapped by seconds between bars
|
||||||
tick_tpl = {
|
tick_tpl = {
|
||||||
60*60*24: '%Y-%b-%d',
|
60*60*24: '%Y-%b-%d',
|
||||||
|
@ -59,24 +85,8 @@ class DynamicDateAxis(pg.AxisItem):
|
||||||
5: '%H:%M:%S',
|
5: '%H:%M:%S',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(
|
def resize(self) -> None:
|
||||||
self,
|
self.setHeight(self.typical_br.height() + 3)
|
||||||
linked_charts,
|
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
) -> None:
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.linked_charts = linked_charts
|
|
||||||
self.setTickFont(_font)
|
|
||||||
self.setPen(_axis_pen)
|
|
||||||
|
|
||||||
# default styling
|
|
||||||
self.setStyle(**{
|
|
||||||
# tickTextOffset=4,
|
|
||||||
'textFillLimits': [(0, 0.666)],
|
|
||||||
'tickFont': _font,
|
|
||||||
})
|
|
||||||
self.setHeight(11)
|
|
||||||
|
|
||||||
def _indexes_to_timestrs(
|
def _indexes_to_timestrs(
|
||||||
self,
|
self,
|
||||||
|
@ -107,7 +117,7 @@ class AxisLabel(pg.GraphicsObject):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
parent: pg.GraphicsObject,
|
parent: Axis,
|
||||||
digits: int = 2,
|
digits: int = 2,
|
||||||
bg_color: str = 'bracket',
|
bg_color: str = 'bracket',
|
||||||
fg_color: str = 'black',
|
fg_color: str = 'black',
|
||||||
|
@ -133,19 +143,6 @@ class AxisLabel(pg.GraphicsObject):
|
||||||
|
|
||||||
self.setFlag(self.ItemIgnoresTransformations)
|
self.setFlag(self.ItemIgnoresTransformations)
|
||||||
|
|
||||||
def _size_br_from_str(self, value: str) -> None:
|
|
||||||
"""Do our best to render the bounding rect to a set margin
|
|
||||||
around provided string contents.
|
|
||||||
|
|
||||||
"""
|
|
||||||
txt_br = self._font._fm.boundingRect(value)
|
|
||||||
h, w = txt_br.height(), txt_br.width()
|
|
||||||
self.rect = QtCore.QRectF(
|
|
||||||
0, 0,
|
|
||||||
w + self._w_margin,
|
|
||||||
h + self._h_margin
|
|
||||||
)
|
|
||||||
|
|
||||||
def paint(self, p, option, widget):
|
def paint(self, p, option, widget):
|
||||||
p.drawPicture(0, 0, self.pic)
|
p.drawPicture(0, 0, self.pic)
|
||||||
|
|
||||||
|
@ -167,15 +164,19 @@ class AxisLabel(pg.GraphicsObject):
|
||||||
def boundingRect(self): # noqa
|
def boundingRect(self): # noqa
|
||||||
return self.rect or QtCore.QRectF()
|
return self.rect or QtCore.QRectF()
|
||||||
|
|
||||||
# uggggghhhh
|
def _size_br_from_str(self, value: str) -> None:
|
||||||
|
"""Do our best to render the bounding rect to a set margin
|
||||||
|
around provided string contents.
|
||||||
|
|
||||||
def tick_to_string(self, tick_pos):
|
"""
|
||||||
raise NotImplementedError()
|
txt_br = self._font._fm.boundingRect(value)
|
||||||
|
txt_h, txt_w = txt_br.height(), txt_br.width()
|
||||||
def update_label(self, evt_post, point_view):
|
h, w = self.size_hint()
|
||||||
raise NotImplementedError()
|
self.rect = QtCore.QRectF(
|
||||||
|
0, 0,
|
||||||
# end uggggghhhh
|
(w or txt_w) + self._w_margin,
|
||||||
|
(h or txt_h) + self._h_margin,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# _common_text_flags = (
|
# _common_text_flags = (
|
||||||
|
@ -190,6 +191,7 @@ class AxisLabel(pg.GraphicsObject):
|
||||||
class XAxisLabel(AxisLabel):
|
class XAxisLabel(AxisLabel):
|
||||||
|
|
||||||
_w_margin = 8
|
_w_margin = 8
|
||||||
|
_h_margin = 0
|
||||||
|
|
||||||
text_flags = (
|
text_flags = (
|
||||||
QtCore.Qt.TextDontClip
|
QtCore.Qt.TextDontClip
|
||||||
|
@ -199,6 +201,10 @@ class XAxisLabel(AxisLabel):
|
||||||
# | QtCore.Qt.AlignHCenter
|
# | QtCore.Qt.AlignHCenter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def size_hint(self) -> Tuple[float, float]:
|
||||||
|
# size to parent axis height
|
||||||
|
return self.parent.height(), None
|
||||||
|
|
||||||
def update_label(
|
def update_label(
|
||||||
self,
|
self,
|
||||||
abs_pos: QPointF, # scene coords
|
abs_pos: QPointF, # scene coords
|
||||||
|
@ -206,8 +212,10 @@ class XAxisLabel(AxisLabel):
|
||||||
offset: int = 1 # if have margins, k?
|
offset: int = 1 # if have margins, k?
|
||||||
) -> None:
|
) -> None:
|
||||||
timestrs = self.parent._indexes_to_timestrs([int(data)])
|
timestrs = self.parent._indexes_to_timestrs([int(data)])
|
||||||
|
|
||||||
if not timestrs.any():
|
if not timestrs.any():
|
||||||
return
|
return
|
||||||
|
|
||||||
self.label_str = timestrs[0]
|
self.label_str = timestrs[0]
|
||||||
width = self.boundingRect().width()
|
width = self.boundingRect().width()
|
||||||
new_pos = QPointF(abs_pos.x() - width / 2 - offset, 0)
|
new_pos = QPointF(abs_pos.x() - width / 2 - offset, 0)
|
||||||
|
@ -222,6 +230,10 @@ class YAxisLabel(AxisLabel):
|
||||||
| QtCore.Qt.AlignVCenter
|
| QtCore.Qt.AlignVCenter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def size_hint(self) -> Tuple[float, float]:
|
||||||
|
# size to parent axis width
|
||||||
|
return None, self.parent.width()
|
||||||
|
|
||||||
def tick_to_string(self, tick_pos):
|
def tick_to_string(self, tick_pos):
|
||||||
# WTF IS THIS FORMAT?
|
# WTF IS THIS FORMAT?
|
||||||
return ('{: ,.%df}' % self.digits).format(tick_pos).replace(',', ' ')
|
return ('{: ,.%df}' % self.digits).format(tick_pos).replace(',', ' ')
|
||||||
|
|
Loading…
Reference in New Issue