from abc import abstractmethod
from logging import getLogger
import numpy as np
from PyQt5 import QtGui
from PyQt5.QtCore import QRectF, Qt, QPointF
from PyQt5.QtGui import QPainter, QImage, QPixmap, QColor
from PyQt5.QtWidgets import QLabel
from ...simu.controllers.base import BaseController
from ...simu.types import InputType, OutputType, Action, State
logger = getLogger(__name__)
[docs]
class TinyLabel(QLabel):
[docs]
def hasHeightForWidth(self) -> bool:
return True
[docs]
@abstractmethod
def heightForWidth(self, w: int) -> int:
return 0
[docs]
class OutputsLabel(TinyLabel):
def __init__(self):
super().__init__()
self.action = None
self.output_type = None
[docs]
def heightForWidth(self, w: int) -> int:
return w
def set_outputs(self, outputs: Action, o_type: OutputType):
self.action = tuple(outputs)
self.output_type = o_type
self.update()
[docs]
def paintEvent(self, _):
if self.action is None:
return
painter = QPainter(self)
w, h = self.width(), self.height()
s = min(w, h)
pen = painter.pen()
pen.setWidthF(0)
painter.setPen(pen)
if self.output_type is OutputType.DISCRETE:
painter.translate(0.5 * (w - s), 0.5 * (h - s))
painter.scale(s / 3 - 1, s / 3 - 1)
painter.drawRect(1, 0, 1, 3)
painter.drawRect(0, 1, 3, 1)
p = {
(+1, 0): (2, 1),
(0, +1): (1, 0),
(-1, 0): (0, 1),
(0, -1): (1, 2),
(0, 0): (1, 1),
}
painter.fillRect(QRectF(*p[self.action], 1, 1), Qt.white)
else:
painter.translate(0.5 * w, 0.5 * h)
painter.scale(0.5 * s, -0.5 * s)
painter.drawLine(-1, 0, 1, 0)
painter.drawLine(0, -1, 0, 1)
pen.setColor(Qt.red)
painter.setPen(pen)
painter.drawLine(QPointF(0, 0), QPointF(*self.action))
[docs]
class ValuesLabel(TinyLabel):
def __init__(self):
super().__init__()
self.setAlignment(Qt.AlignCenter)
self.values = []
[docs]
def heightForWidth(self, w: int) -> int:
return w
def set_values(self, c: BaseController, s: State):
value_f = getattr(c, "values", None)
if value_f is None:
self.setText("N/A")
return
self.setText("")
self.values = value_f(s)
# l, u = c.min_value, c.max_value
l, u = min(self.values), max(self.values)
# r = max(abs(l), abs(u))
# l, u = -r, r
d = u - l
if d != 0:
self.values = [(v, 2 * ((v - l) / d) - 1) for v in self.values]
else:
self.values = [(v, v) for v in self.values]
self.update()
[docs]
def paintEvent(self, e) -> None:
if self.text():
super().paintEvent(e)
return
painter = QPainter(self)
w, h = self.width(), self.height()
size = min(w, h)
s = size / 3
dx, dy = 0.5 * (w - size), 0.5 * (h - size)
painter.translate(dx, dy)
for p, (v, u) in zip([(2, 1), (1, 0), (0, 1), (1, 2)], self.values):
c = QColor.fromRgbF(-u if u < 0 else 0, u if u > 0 else 0, 0)
r = QRectF(p[0] * s, p[1] * s, 1 * s, 1 * s)
painter.setPen(c)
painter.fillRect(r, c)
painter.setPen(Qt.white)
painter.drawText(r, Qt.AlignCenter, f"{v:+.2g}")
[docs]
class ElidedLabel(QLabel):
def __init__(self, *args, mode=Qt.ElideNone, **kwargs):
super().__init__(*args, **kwargs)
self._elide_mode = mode
self._cachedText = ""
self._cachedElidedText = ""
[docs]
def resizeEvent(self, e: QtGui.QResizeEvent):
super().resizeEvent(e)
self._cachedText = ""
[docs]
def paintEvent(self, e: QtGui.QPaintEvent):
if self._elide_mode == Qt.ElideNone:
return super().paintEvent(e)
self._update_cached_texts()
super().setText(self._cachedElidedText)
super().paintEvent(e)
super().setText(self._cachedText)
def _update_cached_texts(self):
txt = self.text()
if txt == self._cachedText:
return
self._cachedText = txt
fm = self.fontMetrics()
self._cachedElidedText = fm.elidedText(
txt, self._elide_mode, self.width(), Qt.TextShowMnemonic
)