Add a sane label type..smh

basic_orders
Tyler Goodlet 2021-02-11 00:12:37 -05:00
parent 386cd9404d
commit 21e1561a57
1 changed files with 188 additions and 0 deletions

188
piker/ui/_label.py 100644
View File

@ -0,0 +1,188 @@
# piker: trading gear for hackers
# Copyright (C) Tyler Goodlet (in stewardship for piker0)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
Non-shitty labels that don't re-invent the wheel.
"""
from typing import Callable
import pyqtgraph as pg
from PyQt5 import QtGui
from PyQt5.QtCore import QPointF, QRectF
from ._style import (
DpiAwareFont,
hcolor,
_down_2_font_inches_we_like,
)
def vbr_left(label) -> float:
def viewbox_left():
return label.vbr().left()
return viewbox_left
def right_axis(chart, label) -> float:
raxis = chart.getAxis('right')
def right_axis_offset_by_w():
return raxis.pos().x() - label.w
return right_axis_offset_by_w
class Label:
"""
After hacking for many days on multiple "label" systems inside
``pyqtgraph`` yet again we're left writing our own since it seems
all of those are over complicated, ad-hoc, pieces of garbage that
can't accomplish the simplest things, such as pinning to the left
hand side of a view box.
This type is another effort (see our graphics lol) to start making
small, re-usable label components that can actually be used to build
production grade UIs.
just.. smh, hard.
"""
def __init__(
self,
view: pg.ViewBox,
fmt_str: str,
color: str = 'bracket',
x_offset: float = 0,
font_size_inches: float = _down_2_font_inches_we_like,
opacity: float = 0.666,
fields: dict = {}
) -> None:
vb = self.vb = view
self._fmt_str = fmt_str
self._view_xy = QPointF(0, 0)
self._x_offset = x_offset
txt = self.txt = QtGui.QGraphicsTextItem()
vb.scene().addItem(txt)
# configure font size based on DPI
dpi_font = DpiAwareFont(
size_in_inches=font_size_inches
)
dpi_font.configure_to_dpi()
txt.setFont(dpi_font.font)
txt.setOpacity(opacity)
# register viewbox callbacks
vb.sigRangeChanged.connect(self.on_sigrange_change)
self._hcolor: str = ''
self.color = color
self.fields = fields
self.orient_v = 'bottom'
self._af = self.txt.pos().x
# TODO: edit and selection support
# https://doc.qt.io/qt-5/qt.html#TextInteractionFlag-enum
# self.setTextInteractionFlags(QtGui.Qt.TextEditorInteraction)
@property
def color(self):
return self._hcolor
@color.setter
def color(self, color: str) -> None:
self.txt.setDefaultTextColor(pg.mkColor(hcolor(color)))
self._hcolor = color
def on_sigrange_change(self, vr, r) -> None:
self.set_view_y(self._view_xy.y())
@property
def w(self) -> float:
return self.txt.boundingRect().width()
@property
def h(self) -> float:
return self.txt.boundingRect().height()
def vbr(self) -> QRectF:
return self.vb.boundingRect()
def set_x_anchor_func(
self,
func: Callable,
) -> None:
assert isinstance(func(), float)
self._af = func
def set_view_y(
self,
y: float,
) -> None:
scene_x = self._af() or self.txt.pos().x()
# get new (inside the) view coordinates / position
self._view_xy = QPointF(
self.vb.mapToView(QPointF(scene_x, scene_x)).x(),
y,
)
# map back to the outer UI-land "scene" coordinates
s_xy = self.vb.mapFromView(self._view_xy)
if self.orient_v == 'top':
s_xy = QPointF(s_xy.x(), s_xy.y() - self.h)
# move label in scene coords to desired position
self.txt.setPos(s_xy)
assert s_xy == self.txt.pos()
def orient_on(self, h: str, v: str) -> None:
pass
@property
def fmt_str(self) -> str:
return self._fmt_str
@fmt_str.setter
def fmt_str(self, fmt_str: str) -> None:
self._fmt_str = fmt_str
def format(self, **fields: dict) -> str:
text = self._fmt_str.format(**fields)
self.txt.setPlainText(text)
def render(self) -> None:
self.format(**self.fields)
def show(self) -> None:
self.txt.show()
def hide(self) -> None:
self.txt.hide()