Size axis labels based on text contents

Compute the size in pixels the label based on the label's contents.
Eventually we want to have an update system that can iterate through
axes and labels to do this whenever needed (eg. after widget is moved
to a new screen with a different DPI).
bar_select
Tyler Goodlet 2020-10-26 23:34:48 -04:00
parent 55f34dfed0
commit 89d48afb6c
1 changed files with 46 additions and 29 deletions

View File

@ -11,6 +11,8 @@ from PyQt5.QtCore import QPointF
from ._style import _font, hcolor from ._style import _font, hcolor
from ..data._source import float_digits from ..data._source import float_digits
_axis_pen = pg.mkPen(hcolor('bracket'))
class PriceAxis(pg.AxisItem): class PriceAxis(pg.AxisItem):
@ -21,15 +23,18 @@ class PriceAxis(pg.AxisItem):
self.setTickFont(_font) self.setTickFont(_font)
self.setStyle(**{ self.setStyle(**{
'textFillLimits': [(0, 0.666)], 'textFillLimits': [(0, 0.666)],
'tickFont': _font,
# 'tickTextWidth': 100, # 'tickTextWidth': 100,
# 'tickTextHeight': 20, # 'tickTextHeight': 20,
'tickFont': _font,
# 'tickTextWidth': 40, # 'tickTextWidth': 40,
# 'autoExpandTextSpace': True, # 'autoExpandTextSpace': True,
# 'maxTickLength': -20, # 'maxTickLength': -20,
# 'stopAxisAtTick': (True, True), # doesn't work well on price # 'stopAxisAtTick': (True, True), # doesn't work well on price
}) })
# self.setLabel(**{'font-size': '10pt'}) # self.setLabel(**{'font-size': '10pt'})
self.setTickFont(_font)
self.setPen(_axis_pen)
self.setWidth(40) self.setWidth(40)
# XXX: drop for now since it just eats up h space # XXX: drop for now since it just eats up h space
@ -63,11 +68,12 @@ class DynamicDateAxis(pg.AxisItem):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.linked_charts = linked_charts self.linked_charts = linked_charts
self.setTickFont(_font) self.setTickFont(_font)
self.setPen(_axis_pen)
# default styling # default styling
self.setStyle(**{ self.setStyle(**{
# tickTextOffset=4, # tickTextOffset=4,
'textFillLimits': [(0, 0.70)], 'textFillLimits': [(0, 0.666)],
'tickFont': _font, 'tickFont': _font,
}) })
self.setHeight(11) self.setHeight(11)
@ -95,6 +101,10 @@ class DynamicDateAxis(pg.AxisItem):
class AxisLabel(pg.GraphicsObject): class AxisLabel(pg.GraphicsObject):
_font = _font
_w_margin = 0
_h_margin = 3
def __init__( def __init__(
self, self,
parent: pg.GraphicsObject, parent: pg.GraphicsObject,
@ -108,6 +118,7 @@ class AxisLabel(pg.GraphicsObject):
self.opacity = opacity self.opacity = opacity
self.label_str = '' self.label_str = ''
self.digits = digits self.digits = digits
self._txt_br: QtCore.QRect = None
self.bg_color = pg.mkColor(hcolor(bg_color)) self.bg_color = pg.mkColor(hcolor(bg_color))
self.fg_color = pg.mkColor(hcolor(fg_color)) self.fg_color = pg.mkColor(hcolor(fg_color))
@ -115,34 +126,52 @@ class AxisLabel(pg.GraphicsObject):
self.pic = QtGui.QPicture() self.pic = QtGui.QPicture()
p = QtGui.QPainter(self.pic) p = QtGui.QPainter(self.pic)
self.rect = QtCore.QRectF(0, 0, 40, 11) self.rect = None
p.setPen(self.fg_color) p.setPen(self.fg_color)
p.setOpacity(self.opacity) p.setOpacity(self.opacity)
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):
p.drawPicture(0, 0, self.pic)
if self.label_str:
if not self.rect:
self._size_br_from_str(self.label_str)
p.setFont(_font)
p.setPen(self.fg_color)
p.fillRect(self.rect, self.bg_color) p.fillRect(self.rect, self.bg_color)
# this adds a nice black outline around the label for some odd # this adds a nice black outline around the label for some odd
# reason; ok by us # reason; ok by us
p.drawRect(self.rect) p.drawRect(self.rect)
self.setFlag(self.ItemIgnoresTransformations) p.drawText(option.rect, self.text_flags, self.label_str)
def paint(self, p, option, widget): def boundingRect(self): # noqa
p.drawPicture(0, 0, self.pic) return self.rect or QtCore.QRectF()
if self.label_str:
p.setFont(_font)
p.setPen(self.fg_color)
p.drawText(self.rect, self.text_flags, self.label_str)
# uggggghhhh # uggggghhhh
def tick_to_string(self, tick_pos): def tick_to_string(self, tick_pos):
raise NotImplementedError() raise NotImplementedError()
def boundingRect(self): # noqa
raise NotImplementedError()
def update_label(self, evt_post, point_view): def update_label(self, evt_post, point_view):
raise NotImplementedError() raise NotImplementedError()
@ -160,6 +189,8 @@ class AxisLabel(pg.GraphicsObject):
class XAxisLabel(AxisLabel): class XAxisLabel(AxisLabel):
_w_margin = 8
text_flags = ( text_flags = (
QtCore.Qt.TextDontClip QtCore.Qt.TextDontClip
| QtCore.Qt.AlignCenter | QtCore.Qt.AlignCenter
@ -168,12 +199,6 @@ class XAxisLabel(AxisLabel):
# | QtCore.Qt.AlignHCenter # | QtCore.Qt.AlignHCenter
) )
def boundingRect(self): # noqa
# TODO: we need to get the parent axe's dimensions transformed
# to abs coords to be 100% correct here:
# self.parent.boundingRect()
return QtCore.QRectF(0, 0, 40, 11)
def update_label( def update_label(
self, self,
abs_pos: QPointF, # scene coords abs_pos: QPointF, # scene coords
@ -201,9 +226,6 @@ class YAxisLabel(AxisLabel):
# WTF IS THIS FORMAT? # WTF IS THIS FORMAT?
return ('{: ,.%df}' % self.digits).format(tick_pos).replace(',', ' ') return ('{: ,.%df}' % self.digits).format(tick_pos).replace(',', ' ')
def boundingRect(self): # noqa
return QtCore.QRectF(0, 0, 50, 10)
def update_label( def update_label(
self, self,
abs_pos: QPointF, # scene coords abs_pos: QPointF, # scene coords
@ -225,15 +247,10 @@ class YSticky(YAxisLabel):
*args, *args,
**kwargs **kwargs
) -> None: ) -> None:
super().__init__(*args, **kwargs)
self._chart = chart
# XXX: not sure why this wouldn't work with a proxy? super().__init__(*args, **kwargs)
# pg.SignalProxy(
# delay=0, self._chart = chart
# rateLimit=60,
# slot=last.update_on_resize,
# )
chart.sigRangeChanged.connect(self.update_on_resize) chart.sigRangeChanged.connect(self.update_on_resize)
def update_on_resize(self, vr, r): def update_on_resize(self, vr, r):