Files
WGSecure/app/ui/history_dialog.py
T
tuxgyverandClaude Opus 5 142eff5916 feat(ui): un thème clair, et plus une couleur hors du module qui les tient
Le module de thème publie deux palettes derrière les mêmes noms : `apply()`
rebranche `BG`, `TEXT`, `TAB_CSS` sur celle qui est choisie au lancement, et
les cent soixante-dix appels répartis dans l'interface continuent de lire
`theme.BG` sans rien savoir du thème actif. Aucun widget n'a eu à changer —
c'est ce que permettait le passage systématique par `theme.X` à l'usage.

Le choix est lu une seule fois, au démarrage. Les feuilles de style sont
posées dans les constructeurs des fenêtres ; les repeindre à chaud
supposerait de reconstruire celle qui porte l'état du tunnel, ses fils
d'exécution et ses minuteries. Le message de confirmation annonce donc la
prise d'effet au prochain lancement plutôt que de laisser croire le contraire.
Le thème sombre reste le défaut, et rend exactement les mêmes feuilles de
style qu'avant.

Un thème clair n'est pas une inversion, et trois familles de valeurs vivaient
en dur là où elles étaient jusqu'ici invisibles. Dans la feuille de style
commune, les variantes d'état des champs et des boutons étaient des voiles
blancs translucides — c'est-à-dire rien du tout sur un fond clair. Le texte
posé sur un aplat saturé — bouton plein, onglet actif, bandeau, sélection —
portait `TEXT`, qui devient presque noir en thème clair et disparaîtrait sur
l'accent : un jeton distinct le dit, blanc dans les deux thèmes. Enfin le
graphe de bande passante peignait son fond en dur, soit un panneau sombre au
milieu d'une page claire ; ses sept teintes sont devenues des jetons, comme
quatre couleurs de survol de la fenêtre principale.

Deux endroits figeaient par ailleurs le thème au chargement de leur module et
auraient gardé les teintes sombres quel que soit le réglage : la feuille de
style de la fenêtre d'historique, bâtie au niveau du module, et la table
état → couleur du diagnostic, évaluée comme attribut de classe. La première
devient une fonction, la seconde ne retient que la puce et demande la couleur
au thème au moment de l'affichage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 20:01:12 +02:00

164 lines
6.0 KiB
Python

"""Fenêtre d'historique des sessions WGSecure."""
from PyQt6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QTableWidget,
QTableWidgetItem, QPushButton, QLabel, QHeaderView,
QMessageBox, QWidget,
)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QColor, QFont
from app.core import history as hist
from app.ui import theme
def _css() -> str:
"""Feuille de style de la fenêtre d'historique.
Fonction et non constante : bâtie à l'import, elle retenait les couleurs
du thème chargé à ce moment-là et ignorait celui appliqué au démarrage.
"""
return f"""
QDialog {{ background: {theme.BG_SUNKEN}; }}
QLabel {{ color: {theme.TEXT}; background: transparent; }}
QTableWidget {{
background: {theme.BG}; color: {theme.TEXT}; gridline-color: {theme.BG_RAISED};
border: none; font-size: 11px;
}}
QHeaderView::section {{
background: {theme.BG_RAISED}; color: {theme.TEXT}; padding: 5px;
border: none; font-weight: bold; font-size: 11px;
}}
QTableWidget::item:selected {{ background: {theme.ACCENT};
color: {theme.ON_SOLID}; }}
QPushButton {{
background: {theme.BG_RAISED}; color: {theme.TEXT}; border-radius: 5px;
padding: 6px 14px; border: none;
}}
QPushButton:hover {{ background: {theme.ACCENT_HOVER};
color: {theme.ON_SOLID}; }}
"""
class HistoryDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("WGSecure — Historique des sessions")
self.setMinimumSize(680, 420)
self.setStyleSheet(_css())
self._build_ui()
self._load()
def _build_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# Bannière
banner = QLabel(" 📊 Historique des sessions VPN")
banner.setFixedHeight(40)
banner.setStyleSheet(
f"background: {theme.BG_SUNKEN}; color: {theme.TEXT}; font-size: 14px; font-weight: bold;"
)
layout.addWidget(banner)
# Tableau
self._table = QTableWidget(0, 6)
self._table.setHorizontalHeaderLabels([
"Date début", "Serveur", "Profil", "Durée", "↓ Reçu", "↑ Envoyé"
])
self._table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
self._table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
self._table.verticalHeader().setVisible(False)
self._table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
self._table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
self._table.setAlternatingRowColors(True)
self._table.setStyleSheet(
_css() +
f"QTableWidget {{ alternate-background-color: {theme.BG_RAISED}; }}"
)
layout.addWidget(self._table)
# Barre infos + boutons
bar = QLabel("")
bar.setFixedHeight(1)
bar.setStyleSheet(f"background: {theme.BG_RAISED};")
layout.addWidget(bar)
btn_row_w = QVBoxLayout()
btn_row_w.setContentsMargins(0, 0, 0, 0)
bottom = QHBoxLayout()
bottom.setContentsMargins(14, 8, 14, 12)
self._summary_lbl = QLabel("")
self._summary_lbl.setStyleSheet(f"color: {theme.TEXT_FAINT}; font-size: 11px;")
bottom.addWidget(self._summary_lbl)
bottom.addStretch()
btn_clear = QPushButton("🗑️ Effacer l'historique")
btn_clear.setStyleSheet(
f"QPushButton {{ background: {theme.FAIL_SOLID}; color: {theme.TEXT}; border-radius: 5px;"
" padding: 6px 14px; border: none; }"
"QPushButton:hover { background: #e74c3c; }"
)
btn_clear.clicked.connect(self._clear)
btn_close = QPushButton("Fermer")
btn_close.clicked.connect(self.accept)
bottom.addWidget(btn_clear)
bottom.addWidget(btn_close)
bottom_w = QWidget()
bottom_w.setStyleSheet(f"background: {theme.BG_SUNKEN};")
bottom_w.setLayout(bottom)
layout.addWidget(bottom_w)
def _load(self):
self._table.setRowCount(0)
sessions = hist.get_sessions(100)
total_rx = total_tx = total_s = 0
for row, s in enumerate(sessions):
self._table.insertRow(row)
end = s.get("end")
color = theme.OK_TEXT if end else theme.WARN_TEXT # vert=terminée, jaune=en cours
cells = [
s.get("start", "—"),
s.get("server", "—"),
s.get("profile", "Défaut"),
hist.fmt_duration(s.get("duration")),
hist.fmt_bytes(s.get("rx", 0)),
hist.fmt_bytes(s.get("tx", 0)),
]
for col, text in enumerate(cells):
item = QTableWidgetItem(text)
item.setForeground(QColor(color))
item.setTextAlignment(
Qt.AlignmentFlag.AlignVCenter |
(Qt.AlignmentFlag.AlignRight if col >= 3
else Qt.AlignmentFlag.AlignLeft)
)
self._table.setItem(row, col, item)
total_rx += s.get("rx", 0)
total_tx += s.get("tx", 0)
if s.get("duration"):
total_s += s["duration"]
n = len(sessions)
self._summary_lbl.setText(
f"{n} session{'s' if n > 1 else ''} • "
f"Total ↓ {hist.fmt_bytes(total_rx)} "
f"↑ {hist.fmt_bytes(total_tx)} • "
f"Durée cumulée : {hist.fmt_duration(total_s)}"
)
def _clear(self):
reply = QMessageBox.question(
self, "Confirmer",
"Effacer tout l'historique des sessions ?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
)
if reply == QMessageBox.StandardButton.Yes:
hist.clear()
self._load()