Update
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""Widget graphique bande passante RX/TX (QPainter, sans dépendance externe)."""
|
||||
from __future__ import annotations
|
||||
from collections import deque
|
||||
from PyQt6.QtWidgets import QWidget
|
||||
from PyQt6.QtGui import QPainter, QColor, QPen, QBrush, QFont, QPainterPath
|
||||
from PyQt6.QtCore import Qt, QRect, QPointF
|
||||
|
||||
|
||||
def _fmt(bps: float) -> str:
|
||||
if bps >= 1_048_576:
|
||||
return f"{bps/1_048_576:.1f} MB/s"
|
||||
if bps >= 1_024:
|
||||
return f"{bps/1_024:.0f} KB/s"
|
||||
return f"{bps:.0f} B/s"
|
||||
|
||||
|
||||
class BandwidthGraph(QWidget):
|
||||
"""Affiche RX (bleu) et TX (vert) sur les N dernières secondes."""
|
||||
|
||||
POINTS = 30 # nombre de points conservés
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._rx: deque[float] = deque([0.0] * self.POINTS, maxlen=self.POINTS)
|
||||
self._tx: deque[float] = deque([0.0] * self.POINTS, maxlen=self.POINTS)
|
||||
self.setMinimumHeight(70)
|
||||
self.setStyleSheet("background: transparent;")
|
||||
|
||||
def push(self, rx_bps: float, tx_bps: float) -> None:
|
||||
self._rx.append(max(0.0, rx_bps))
|
||||
self._tx.append(max(0.0, tx_bps))
|
||||
self.update()
|
||||
|
||||
def reset(self) -> None:
|
||||
self._rx = deque([0.0] * self.POINTS, maxlen=self.POINTS)
|
||||
self._tx = deque([0.0] * self.POINTS, maxlen=self.POINTS)
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, _):
|
||||
p = QPainter(self)
|
||||
p.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
w, h = self.width(), self.height()
|
||||
pad_l, pad_r, pad_t, pad_b = 42, 8, 6, 20
|
||||
|
||||
# Fond
|
||||
p.fillRect(0, 0, w, h, QColor("#17202a"))
|
||||
|
||||
plot_w = w - pad_l - pad_r
|
||||
plot_h = h - pad_t - pad_b
|
||||
|
||||
max_val = max(max(self._rx), max(self._tx), 1.0)
|
||||
|
||||
def _path(data: deque, color: str, fill: str):
|
||||
pts = list(data)
|
||||
path = QPainterPath()
|
||||
xs = [pad_l + i * plot_w / (len(pts) - 1) for i in range(len(pts))]
|
||||
ys = [pad_t + plot_h - (v / max_val) * plot_h for v in pts]
|
||||
path.moveTo(QPointF(xs[0], pad_t + plot_h))
|
||||
path.lineTo(QPointF(xs[0], ys[0]))
|
||||
for x, y in zip(xs[1:], ys[1:]):
|
||||
path.lineTo(QPointF(x, y))
|
||||
path.lineTo(QPointF(xs[-1], pad_t + plot_h))
|
||||
path.closeSubpath()
|
||||
p.fillPath(path, QBrush(QColor(fill)))
|
||||
pen = QPen(QColor(color))
|
||||
pen.setWidthF(1.5)
|
||||
p.setPen(pen)
|
||||
line = QPainterPath()
|
||||
line.moveTo(QPointF(xs[0], ys[0]))
|
||||
for x, y in zip(xs[1:], ys[1:]):
|
||||
line.lineTo(QPointF(x, y))
|
||||
p.drawPath(line)
|
||||
|
||||
_path(self._rx, "#5dade2", "#1a3a52") # RX bleu
|
||||
_path(self._tx, "#58d68d", "#1a3d2b") # TX vert
|
||||
|
||||
# Axes
|
||||
p.setPen(QPen(QColor("#2e4057")))
|
||||
p.drawLine(pad_l, pad_t, pad_l, pad_t + plot_h)
|
||||
p.drawLine(pad_l, pad_t + plot_h, w - pad_r, pad_t + plot_h)
|
||||
|
||||
# Labels Y
|
||||
font = QFont("Arial", 8)
|
||||
p.setFont(font)
|
||||
p.setPen(QPen(QColor("#5d6d7e")))
|
||||
for frac, label in [(0.0, _fmt(max_val)), (0.5, _fmt(max_val / 2)), (1.0, "0")]:
|
||||
y = int(pad_t + frac * plot_h)
|
||||
p.drawText(QRect(0, y - 8, pad_l - 2, 16),
|
||||
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
|
||||
label)
|
||||
|
||||
# Légende
|
||||
p.setPen(QPen(QColor("#5dade2")))
|
||||
p.drawText(pad_l + 4, pad_t + 12, f"↓ {_fmt(self._rx[-1])}")
|
||||
p.setPen(QPen(QColor("#58d68d")))
|
||||
p.drawText(pad_l + 90, pad_t + 12, f"↑ {_fmt(self._tx[-1])}")
|
||||
|
||||
p.end()
|
||||
Reference in New Issue
Block a user