- Show charge/discharge power in watts, a battery graphic with charge bolt, a power history graph and the estimated time until full or remaining (respecting the 80 % charge limit) in the app, the panel widget, its tooltip and `predatorctl status` - Add packaging/arch/PKGBUILD that builds a pacman package; the kernel module ships as DKMS source and is rebuilt for every kernel - Bump version to 1.1.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
527 lines
19 KiB
Python
527 lines
19 KiB
Python
"""Custom widgets: gauges, fan curve editor, per-key keyboard painter, colour swatches."""
|
||
|
||
from PySide6.QtCore import QPointF, QRectF, Qt, Signal
|
||
from PySide6.QtGui import QColor, QFont, QPainter, QPainterPath, QPen
|
||
from PySide6.QtWidgets import (QColorDialog, QFrame, QHBoxLayout, QLabel, QPushButton,
|
||
QSizePolicy, QVBoxLayout, QWidget)
|
||
|
||
from .. import layout
|
||
from . import theme
|
||
|
||
|
||
class Card(QFrame):
|
||
"""Rounded panel with an optional title."""
|
||
|
||
def __init__(self, title=None, parent=None):
|
||
super().__init__(parent)
|
||
self.setObjectName("card")
|
||
self.body = QVBoxLayout(self)
|
||
self.body.setContentsMargins(18, 16, 18, 16)
|
||
self.body.setSpacing(10)
|
||
if title:
|
||
t = QLabel(title)
|
||
t.setObjectName("cardTitle")
|
||
self.body.addWidget(t)
|
||
|
||
|
||
class Gauge(QWidget):
|
||
"""Circular gauge (270° arc)."""
|
||
|
||
def __init__(self, title, unit, maximum=100, warn=None, parent=None):
|
||
super().__init__(parent)
|
||
self.title, self.unit, self.maximum, self.warn = title, unit, maximum, warn
|
||
self.value = None
|
||
self.sub = ""
|
||
self.setMinimumSize(150, 150)
|
||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||
|
||
def set_value(self, value, sub=""):
|
||
self.value, self.sub = value, sub
|
||
self.update()
|
||
|
||
def paintEvent(self, _):
|
||
p = QPainter(self)
|
||
p.setRenderHint(QPainter.Antialiasing)
|
||
side = min(self.width(), self.height()) - 16
|
||
r = QRectF((self.width() - side) / 2, (self.height() - side) / 2, side, side)
|
||
pen = QPen(QColor(theme.TRACK), max(8, side / 14), Qt.SolidLine, Qt.RoundCap)
|
||
p.setPen(pen)
|
||
p.drawArc(r, -45 * 16, 270 * 16)
|
||
if self.value is not None:
|
||
frac = max(0.0, min(1.0, self.value / self.maximum))
|
||
col = QColor(theme.ACCENT)
|
||
if self.warn and self.value >= self.warn:
|
||
col = QColor(theme.DANGER)
|
||
elif self.warn and self.value >= self.warn * 0.85:
|
||
col = QColor(theme.WARN)
|
||
pen.setColor(col)
|
||
p.setPen(pen)
|
||
p.drawArc(r, 225 * 16, int(-270 * 16 * frac))
|
||
p.setPen(QColor(theme.TEXT))
|
||
f = QFont(self.font())
|
||
f.setPointSizeF(max(10, side / 7))
|
||
f.setBold(True)
|
||
p.setFont(f)
|
||
txt = "–" if self.value is None else ("%d" % round(self.value))
|
||
p.drawText(r.adjusted(0, -side * 0.08, 0, -side * 0.08), Qt.AlignCenter, txt)
|
||
f.setPointSizeF(max(7, side / 16))
|
||
f.setBold(False)
|
||
p.setFont(f)
|
||
p.setPen(QColor(theme.MUTED))
|
||
p.drawText(r.adjusted(0, side * 0.22, 0, 0), Qt.AlignHCenter | Qt.AlignVCenter, self.unit)
|
||
p.setPen(QColor(theme.TEXT))
|
||
p.drawText(QRectF(r.left(), r.bottom() - side * 0.2, r.width(), side * 0.2),
|
||
Qt.AlignHCenter | Qt.AlignVCenter, self.title)
|
||
if self.sub:
|
||
p.setPen(QColor(theme.MUTED))
|
||
p.drawText(QRectF(r.left(), r.bottom() - side * 0.05, r.width(), side * 0.2),
|
||
Qt.AlignHCenter | Qt.AlignTop, self.sub)
|
||
|
||
|
||
class Bar(QWidget):
|
||
"""Thin labelled progress bar."""
|
||
|
||
def __init__(self, title, parent=None):
|
||
super().__init__(parent)
|
||
self.title = title
|
||
self.value, self.text = 0, ""
|
||
self.setMinimumHeight(38)
|
||
|
||
def set_value(self, frac, text):
|
||
self.value, self.text = max(0.0, min(1.0, frac or 0)), text
|
||
self.update()
|
||
|
||
def paintEvent(self, _):
|
||
p = QPainter(self)
|
||
p.setRenderHint(QPainter.Antialiasing)
|
||
p.setPen(QColor(theme.MUTED))
|
||
p.drawText(QRectF(0, 0, self.width(), 18), Qt.AlignLeft | Qt.AlignVCenter, self.title)
|
||
p.setPen(QColor(theme.TEXT))
|
||
p.drawText(QRectF(0, 0, self.width(), 18), Qt.AlignRight | Qt.AlignVCenter, self.text)
|
||
track = QRectF(0, 24, self.width(), 8)
|
||
p.setPen(Qt.NoPen)
|
||
p.setBrush(QColor(theme.TRACK))
|
||
p.drawRoundedRect(track, 4, 4)
|
||
p.setBrush(QColor(theme.ACCENT))
|
||
p.drawRoundedRect(QRectF(0, 24, self.width() * self.value, 8), 4, 4)
|
||
|
||
|
||
class CurveEditor(QWidget):
|
||
"""Drag-and-drop fan curve: temperature (°C) -> fan speed (%)."""
|
||
|
||
changed = Signal(list)
|
||
T_MIN, T_MAX = 30, 100
|
||
|
||
def __init__(self, title, parent=None):
|
||
super().__init__(parent)
|
||
self.title = title
|
||
self.points = [[45, 0], [60, 35], [72, 55], [82, 75], [90, 100]]
|
||
self.current_temp = None
|
||
self._drag = None
|
||
self.setMinimumHeight(220)
|
||
self.setMouseTracking(True)
|
||
|
||
def set_points(self, pts):
|
||
if self._drag is None:
|
||
self.points = sorted([list(p) for p in pts])
|
||
self.update()
|
||
|
||
def set_current(self, temp):
|
||
self.current_temp = temp
|
||
self.update()
|
||
|
||
def _plot(self):
|
||
return QRectF(44, 26, self.width() - 60, self.height() - 56)
|
||
|
||
def _to_px(self, t, s):
|
||
r = self._plot()
|
||
return QPointF(r.left() + (t - self.T_MIN) / (self.T_MAX - self.T_MIN) * r.width(),
|
||
r.bottom() - s / 100 * r.height())
|
||
|
||
def _from_px(self, pos):
|
||
r = self._plot()
|
||
t = self.T_MIN + (pos.x() - r.left()) / r.width() * (self.T_MAX - self.T_MIN)
|
||
s = (r.bottom() - pos.y()) / r.height() * 100
|
||
return t, s
|
||
|
||
def paintEvent(self, _):
|
||
p = QPainter(self)
|
||
p.setRenderHint(QPainter.Antialiasing)
|
||
r = self._plot()
|
||
p.setPen(QColor(theme.TEXT))
|
||
p.drawText(QRectF(0, 0, self.width(), 20), Qt.AlignLeft | Qt.AlignVCenter, self.title)
|
||
grid = QPen(QColor(theme.GRID), 1)
|
||
p.setPen(grid)
|
||
for s in range(0, 101, 25):
|
||
a = self._to_px(self.T_MIN, s)
|
||
p.drawLine(QPointF(r.left(), a.y()), QPointF(r.right(), a.y()))
|
||
p.setPen(QColor(theme.MUTED))
|
||
p.drawText(QRectF(0, a.y() - 8, 38, 16), Qt.AlignRight | Qt.AlignVCenter, "%d%%" % s)
|
||
p.setPen(grid)
|
||
for t in range(self.T_MIN, self.T_MAX + 1, 10):
|
||
a = self._to_px(t, 0)
|
||
p.drawLine(QPointF(a.x(), r.top()), QPointF(a.x(), r.bottom()))
|
||
p.setPen(QColor(theme.MUTED))
|
||
p.drawText(QRectF(a.x() - 20, r.bottom() + 4, 40, 16), Qt.AlignCenter, "%d°" % t)
|
||
p.setPen(grid)
|
||
if self.current_temp:
|
||
a = self._to_px(max(self.T_MIN, min(self.T_MAX, self.current_temp)), 0)
|
||
p.setPen(QPen(QColor(theme.WARN), 1, Qt.DashLine))
|
||
p.drawLine(QPointF(a.x(), r.top()), QPointF(a.x(), r.bottom()))
|
||
pts = sorted(self.points)
|
||
path = QPainterPath(self._to_px(self.T_MIN, pts[0][1]))
|
||
for t, s in pts:
|
||
path.lineTo(self._to_px(t, s))
|
||
path.lineTo(self._to_px(self.T_MAX, pts[-1][1]))
|
||
fill = QPainterPath(path)
|
||
fill.lineTo(self._to_px(self.T_MAX, 0))
|
||
fill.lineTo(self._to_px(self.T_MIN, 0))
|
||
c = QColor(theme.ACCENT)
|
||
c.setAlpha(40)
|
||
p.fillPath(fill, c)
|
||
p.setPen(QPen(QColor(theme.ACCENT), 2.5))
|
||
p.drawPath(path)
|
||
p.setBrush(QColor(theme.BG))
|
||
for i, (t, s) in enumerate(pts):
|
||
p.setPen(QPen(QColor(theme.TEXT if i == self._drag else theme.ACCENT), 2.5))
|
||
p.drawEllipse(self._to_px(t, s), 6, 6)
|
||
if i == self._drag:
|
||
p.drawText(self._to_px(t, s) + QPointF(10, -10), "%d° → %d%%" % (t, s))
|
||
|
||
def mousePressEvent(self, e):
|
||
for i, (t, s) in enumerate(sorted(self.points)):
|
||
d = self._to_px(t, s) - e.position()
|
||
if abs(d.x()) < 12 and abs(d.y()) < 12:
|
||
self.points = sorted(self.points)
|
||
self._drag = i
|
||
self.update()
|
||
return
|
||
|
||
def mouseMoveEvent(self, e):
|
||
if self._drag is None:
|
||
return
|
||
t, s = self._from_px(e.position())
|
||
i = self._drag
|
||
lo = self.points[i - 1][0] + 1 if i > 0 else self.T_MIN
|
||
hi = self.points[i + 1][0] - 1 if i < len(self.points) - 1 else self.T_MAX
|
||
self.points[i] = [int(max(lo, min(hi, round(t)))), int(max(0, min(100, round(s / 5) * 5)))]
|
||
self.update()
|
||
|
||
def mouseReleaseEvent(self, _):
|
||
if self._drag is not None:
|
||
self._drag = None
|
||
self.update()
|
||
self.changed.emit([list(p) for p in self.points])
|
||
|
||
|
||
class KeyboardPainter(QWidget):
|
||
"""Draws the keyboard and lets the user paint keys with the current colour."""
|
||
|
||
painted = Signal()
|
||
picked = Signal(QColor)
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.variant = layout.default_variant()
|
||
self.keys = layout.keys(self.variant)
|
||
self.colors = {} # cell -> QColor
|
||
self.brush = QColor(theme.ACCENT)
|
||
self._painting = False
|
||
self._hover = None
|
||
self.setMouseTracking(True)
|
||
self.setMinimumHeight(260)
|
||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||
|
||
def set_variant(self, variant):
|
||
self.variant = variant
|
||
self.keys = layout.keys(variant)
|
||
self.update()
|
||
|
||
def heightForWidth(self, w):
|
||
kw, kh = layout.size(self.keys)
|
||
return int(w * kh / kw)
|
||
|
||
def hasHeightForWidth(self):
|
||
return True
|
||
|
||
def _scale(self):
|
||
kw, kh = layout.size(self.keys)
|
||
s = min((self.width() - 8) / kw, (self.height() - 8) / kh)
|
||
ox = (self.width() - kw * s) / 2
|
||
oy = (self.height() - kh * s) / 2
|
||
return s, ox, oy
|
||
|
||
def _rect(self, k, s, ox, oy):
|
||
_l, _c, x, y, w, h = k
|
||
g = s * 0.08
|
||
return QRectF(ox + x * s + g / 2, oy + y * s + g / 2, w * s - g, h * s - g)
|
||
|
||
def _key_at(self, pos):
|
||
s, ox, oy = self._scale()
|
||
for k in self.keys:
|
||
if self._rect(k, s, ox, oy).contains(pos):
|
||
return k
|
||
return None
|
||
|
||
def paintEvent(self, _):
|
||
p = QPainter(self)
|
||
p.setRenderHint(QPainter.Antialiasing)
|
||
s, ox, oy = self._scale()
|
||
f = QFont(self.font())
|
||
for k in self.keys:
|
||
label, cell = k[0], k[1]
|
||
r = self._rect(k, s, ox, oy)
|
||
col = self.colors.get(cell, QColor(0, 0, 0))
|
||
base = QColor(theme.KEY)
|
||
p.setPen(QPen(QColor(theme.TEXT) if k is self._hover else QColor(theme.KEY_BORDER), 1.5))
|
||
p.setBrush(base)
|
||
p.drawRoundedRect(r, s * 0.12, s * 0.12)
|
||
# glow of the key colour
|
||
glow = QColor(col)
|
||
glow.setAlpha(210 if (col.red() + col.green() + col.blue()) > 0 else 0)
|
||
p.setPen(Qt.NoPen)
|
||
p.setBrush(glow)
|
||
p.drawRoundedRect(r.adjusted(3, 3, -3, -3), s * 0.1, s * 0.1)
|
||
lum = 0.299 * col.red() + 0.587 * col.green() + 0.114 * col.blue()
|
||
p.setPen(QColor("#0b0f14") if lum > 150 else QColor(theme.TEXT))
|
||
f.setPointSizeF(max(6, s * (0.22 if len(label) <= 2 else 0.16)))
|
||
p.setFont(f)
|
||
p.drawText(r, Qt.AlignCenter, label)
|
||
|
||
def _paint_at(self, pos, button):
|
||
k = self._key_at(pos)
|
||
if not k:
|
||
return
|
||
if button == Qt.RightButton:
|
||
self.picked.emit(QColor(self.colors.get(k[1], QColor(0, 0, 0))))
|
||
return
|
||
if self.colors.get(k[1]) != self.brush:
|
||
self.colors[k[1]] = QColor(self.brush)
|
||
self.update()
|
||
self.painted.emit()
|
||
|
||
def mousePressEvent(self, e):
|
||
self._painting = e.button() == Qt.LeftButton
|
||
self._paint_at(e.position(), e.button())
|
||
|
||
def mouseMoveEvent(self, e):
|
||
k = self._key_at(e.position())
|
||
if k is not self._hover:
|
||
self._hover = k
|
||
self.update()
|
||
if self._painting:
|
||
self._paint_at(e.position(), Qt.LeftButton)
|
||
|
||
def mouseReleaseEvent(self, _):
|
||
self._painting = False
|
||
|
||
def leaveEvent(self, _):
|
||
self._hover = None
|
||
self.update()
|
||
|
||
def fill(self, cells, color):
|
||
for c in cells:
|
||
self.colors[c] = QColor(color)
|
||
self.update()
|
||
self.painted.emit()
|
||
|
||
def frame_hex(self):
|
||
out = bytearray(128 * 3)
|
||
for c, col in self.colors.items():
|
||
if 0 <= c < 128:
|
||
out[c * 3:c * 3 + 3] = bytes((col.red(), col.green(), col.blue()))
|
||
return out.hex()
|
||
|
||
def load_frame(self, hexstr):
|
||
self.colors = {}
|
||
try:
|
||
data = bytes.fromhex(hexstr or "")
|
||
except ValueError:
|
||
data = b""
|
||
for c in range(min(128, len(data) // 3)):
|
||
self.colors[c] = QColor(data[c * 3], data[c * 3 + 1], data[c * 3 + 2])
|
||
self.update()
|
||
|
||
|
||
PRESET_COLORS = ["#00d4ff", "#0057ff", "#7a00ff", "#ff00c8", "#ff0033", "#ff6a00",
|
||
"#ffd000", "#3cff00", "#00ff9c", "#ffffff"]
|
||
|
||
|
||
class ColorRow(QWidget):
|
||
"""Row of colour swatches plus a 'custom colour' button."""
|
||
|
||
colorChosen = Signal(QColor)
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
lay = QHBoxLayout(self)
|
||
lay.setContentsMargins(0, 0, 0, 0)
|
||
lay.setSpacing(6)
|
||
self.current = QColor(PRESET_COLORS[0])
|
||
for c in PRESET_COLORS:
|
||
b = QPushButton()
|
||
b.setObjectName("swatch")
|
||
b.setFixedSize(28, 28)
|
||
b.setStyleSheet("QPushButton#swatch{background:%s;}" % c)
|
||
b.setToolTip(c)
|
||
b.clicked.connect(lambda _=False, c=c: self._choose(QColor(c)))
|
||
lay.addWidget(b)
|
||
self.custom = QPushButton("Custom…")
|
||
self.custom.clicked.connect(self._dialog)
|
||
lay.addWidget(self.custom)
|
||
self.preview = QLabel()
|
||
self.preview.setFixedSize(46, 28)
|
||
lay.addWidget(self.preview)
|
||
lay.addStretch(1)
|
||
self.set_color(self.current)
|
||
|
||
def _dialog(self):
|
||
c = QColorDialog.getColor(self.current, self, "Choose colour")
|
||
if c.isValid():
|
||
self._choose(c)
|
||
|
||
def _choose(self, c):
|
||
self.set_color(c)
|
||
self.colorChosen.emit(c)
|
||
|
||
def set_color(self, c):
|
||
self.current = QColor(c)
|
||
self.preview.setStyleSheet("background:%s;border-radius:6px;border:1px solid %s;"
|
||
% (self.current.name(), theme.BORDER))
|
||
|
||
|
||
def format_minutes(m):
|
||
if m is None:
|
||
return ""
|
||
return "%d h %02d min" % divmod(m, 60) if m >= 60 else "%d min" % m
|
||
|
||
|
||
class ChargeIndicator(QWidget):
|
||
"""Battery graphic with fill level, charge bolt, wattage and a power history sparkline."""
|
||
|
||
HISTORY = 80 # samples (~2 min at 1.5 s)
|
||
|
||
def __init__(self, compact=False, parent=None):
|
||
super().__init__(parent)
|
||
self.compact = compact
|
||
self.bat = {}
|
||
self.history = []
|
||
self.setMinimumHeight(54 if compact else 190)
|
||
self.setMinimumWidth(300 if compact else 420)
|
||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||
|
||
def set_battery(self, bat):
|
||
self.bat = bat or {}
|
||
w = self.bat.get("power_w")
|
||
if w is not None:
|
||
self.history = (self.history + [w])[-self.HISTORY:]
|
||
self.update()
|
||
|
||
def _state_text(self):
|
||
b = self.bat
|
||
st = b.get("status") or ""
|
||
if st == "Charging":
|
||
return "Charging"
|
||
if st == "Discharging":
|
||
return "On battery"
|
||
if b.get("ac_online"):
|
||
return "Plugged in · not charging"
|
||
return st or "Unknown"
|
||
|
||
def paintEvent(self, _):
|
||
b = self.bat
|
||
p = QPainter(self)
|
||
p.setRenderHint(QPainter.Antialiasing)
|
||
h = self.height() - 8 if self.compact else min(self.height() * 0.42, 86)
|
||
bw, bh = h * 2.0, h
|
||
r = QRectF(4, 4, bw, bh)
|
||
cap = b.get("capacity") or 0
|
||
charging = b.get("charging")
|
||
accent = QColor(theme.ACCENT if charging else (theme.DANGER if cap <= 15 else theme.TEXT))
|
||
|
||
# battery body + tip
|
||
p.setPen(QPen(QColor(theme.MUTED), 2.5))
|
||
p.setBrush(Qt.NoBrush)
|
||
p.drawRoundedRect(r, h * 0.14, h * 0.14)
|
||
p.setPen(Qt.NoPen)
|
||
p.setBrush(QColor(theme.MUTED))
|
||
p.drawRoundedRect(QRectF(r.right() + 2, r.top() + bh * 0.3, bh * 0.1, bh * 0.4), 2, 2)
|
||
inner = r.adjusted(5, 5, -5, -5)
|
||
fill = QColor(accent)
|
||
fill.setAlpha(170)
|
||
p.setBrush(fill)
|
||
p.drawRoundedRect(QRectF(inner.left(), inner.top(), inner.width() * cap / 100, inner.height()),
|
||
h * 0.08, h * 0.08)
|
||
if charging:
|
||
c = inner.center()
|
||
s = inner.height() * 0.42
|
||
bolt = QPainterPath(QPointF(c.x() + s * 0.15, c.y() - s))
|
||
bolt.lineTo(c.x() - s * 0.45, c.y() + s * 0.12)
|
||
bolt.lineTo(c.x() - s * 0.02, c.y() + s * 0.12)
|
||
bolt.lineTo(c.x() - s * 0.15, c.y() + s)
|
||
bolt.lineTo(c.x() + s * 0.45, c.y() - s * 0.12)
|
||
bolt.lineTo(c.x() + s * 0.02, c.y() - s * 0.12)
|
||
bolt.closeSubpath()
|
||
p.setBrush(QColor(theme.TEXT))
|
||
p.setPen(QPen(QColor(theme.BG), 1.5))
|
||
p.drawPath(bolt)
|
||
else:
|
||
f = QFont(self.font())
|
||
f.setBold(True)
|
||
f.setPointSizeF(max(8, h * 0.22))
|
||
p.setFont(f)
|
||
p.setPen(QColor(theme.TEXT))
|
||
p.drawText(inner, Qt.AlignCenter, "%d %%" % cap)
|
||
|
||
# text block
|
||
tx = r.right() + bh * 0.1 + 16
|
||
f = QFont(self.font())
|
||
w = b.get("power_w")
|
||
f.setBold(True)
|
||
f.setPointSizeF(max(10, h * (0.36 if self.compact else 0.34)))
|
||
p.setFont(f)
|
||
p.setPen(accent if charging else QColor(theme.TEXT))
|
||
big = ("%.1f W" % w) if w else ("%d %%" % cap)
|
||
p.drawText(QRectF(tx, r.top(), self.width() - tx, bh * 0.55), Qt.AlignLeft | Qt.AlignVCenter, big)
|
||
f.setBold(False)
|
||
f.setPointSizeF(max(8, h * (0.17 if self.compact else 0.14)))
|
||
p.setFont(f)
|
||
p.setPen(QColor(theme.MUTED))
|
||
sub = self._state_text()
|
||
if w:
|
||
sub = "%d %% · %s" % (cap, sub)
|
||
t = format_minutes(b.get("minutes_left"))
|
||
if t:
|
||
sub += " · %s %s" % (t, "until full" if charging else "left")
|
||
p.drawText(QRectF(tx, r.top() + bh * 0.5, self.width() - tx, bh * 0.5), Qt.AlignLeft | Qt.AlignVCenter, sub)
|
||
|
||
if self.compact or len(self.history) < 2:
|
||
return
|
||
# power history sparkline
|
||
g = QRectF(4, r.bottom() + 22, self.width() - 8, self.height() - r.bottom() - 30)
|
||
if g.height() < 30:
|
||
return
|
||
p.setPen(QColor(theme.MUTED))
|
||
f.setPointSizeF(8)
|
||
p.setFont(f)
|
||
top = max(10.0, max(self.history) * 1.15)
|
||
p.drawText(QRectF(g.left(), g.top() - 18, g.width(), 16), Qt.AlignLeft,
|
||
"Power over the last %d min" % max(1, round(len(self.history) * 1.5 / 60)))
|
||
p.drawText(QRectF(g.left(), g.top() - 18, g.width(), 16), Qt.AlignRight, "max %.0f W" % top)
|
||
p.setPen(QPen(QColor(theme.GRID), 1))
|
||
p.drawLine(g.bottomLeft(), g.bottomRight())
|
||
n = self.HISTORY - 1
|
||
pts = [QPointF(g.left() + g.width() * (n - (len(self.history) - 1 - i)) / n,
|
||
g.bottom() - g.height() * v / top) for i, v in enumerate(self.history)]
|
||
path = QPainterPath(pts[0])
|
||
for pt in pts[1:]:
|
||
path.lineTo(pt)
|
||
area = QPainterPath(path)
|
||
area.lineTo(pts[-1].x(), g.bottom())
|
||
area.lineTo(pts[0].x(), g.bottom())
|
||
c = QColor(theme.ACCENT)
|
||
c.setAlpha(45)
|
||
p.fillPath(area, c)
|
||
p.setPen(QPen(QColor(theme.ACCENT), 2))
|
||
p.drawPath(path)
|