feat: dark theme toggle + CPU/memory sparkline trend charts
- ui/theme.py: light/dark QSS stylesheets, persisted to ~/.sshclient/settings.json - main_window.py: View menu with dark theme checkbox, applies on startup - ui/widgets.py: SparklineChart widget for mini trend visualization - MonitorPanel: CPU and memory sparkline charts showing last 60 samples - Charts cleared on stop/switch host
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
主题管理:亮色 / 暗色 QSS 样式表
|
||||
持久化到 ~/.sshclient/settings.json
|
||||
"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG_DIR = Path.home() / ".sshclient"
|
||||
SETTINGS_FILE = CONFIG_DIR / "settings.json"
|
||||
|
||||
DARK_QSS = """
|
||||
QMainWindow, QWidget { background-color: #1e1e2e; color: #cdd6f4; }
|
||||
QMenuBar { background-color: #181825; color: #cdd6f4; border-bottom: 1px solid #313244; }
|
||||
QMenuBar::item:selected { background-color: #313244; }
|
||||
QMenu { background-color: #1e1e2e; color: #cdd6f4; border: 1px solid #313244; }
|
||||
QMenu::item:selected { background-color: #45475a; }
|
||||
QTabWidget::pane { border: 1px solid #313244; background: #1e1e2e; }
|
||||
QTabBar::tab { background: #181825; color: #a6adc8; padding: 6px 14px; border: 1px solid #313244; border-bottom: none; }
|
||||
QTabBar::tab:selected { background: #1e1e2e; color: #89b4fa; border-bottom: 2px solid #89b4fa; }
|
||||
QTabBar::tab:hover:!selected { background: #313244; }
|
||||
QListWidget { background-color: #181825; color: #cdd6f4; border: 1px solid #313244; alternate-background-color: #1e1e2e; }
|
||||
QListWidget::item:selected { background-color: #45475a; color: #89b4fa; }
|
||||
QPushButton { background-color: #313244; color: #cdd6f4; border: 1px solid #45475a; padding: 5px 12px; border-radius: 3px; }
|
||||
QPushButton:hover { background-color: #45475a; border-color: #89b4fa; }
|
||||
QPushButton:pressed { background-color: #585b70; }
|
||||
QPushButton:disabled { color: #585b70; background-color: #1e1e2e; }
|
||||
QLineEdit, QTextEdit, QPlainTextEdit, QComboBox, QSpinBox, QDoubleSpinBox {
|
||||
background-color: #181825; color: #cdd6f4; border: 1px solid #313244; padding: 3px 6px; border-radius: 2px;
|
||||
}
|
||||
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus, QComboBox:focus { border-color: #89b4fa; }
|
||||
QComboBox::drop-down { border: none; }
|
||||
QComboBox QAbstractItemView { background-color: #1e1e2e; color: #cdd6f4; selection-background-color: #45475a; }
|
||||
QTableWidget { background-color: #181825; color: #cdd6f4; gridline-color: #313244; alternate-background-color: #1e1e2e; }
|
||||
QTableWidget::item:selected { background-color: #45475a; }
|
||||
QHeaderView::section { background-color: #313244; color: #cdd6f4; border: none; padding: 4px; border-right: 1px solid #45475a; }
|
||||
QTreeWidget { background-color: #181825; color: #cdd6f4; border: 1px solid #313244; }
|
||||
QTreeWidget::item:selected { background-color: #45475a; color: #89b4fa; }
|
||||
QProgressBar { background-color: #181825; border: 1px solid #313244; border-radius: 3px; text-align: center; color: #cdd6f4; }
|
||||
QProgressBar::chunk { background-color: #89b4fa; border-radius: 2px; }
|
||||
QGroupBox { border: 1px solid #313244; border-radius: 4px; margin-top: 8px; padding-top: 8px; color: #89b4fa; font-weight: bold; }
|
||||
QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 4px; }
|
||||
QLabel { color: #cdd6f4; }
|
||||
QStatusBar { background-color: #181825; color: #a6adc8; }
|
||||
QSplitter::handle { background-color: #313244; }
|
||||
QSplitter::handle:horizontal { width: 2px; }
|
||||
QSplitter::handle:vertical { height: 2px; }
|
||||
QScrollBar:vertical { background: #181825; width: 10px; border: none; }
|
||||
QScrollBar::handle:vertical { background: #45475a; min-height: 20px; border-radius: 4px; }
|
||||
QScrollBar::handle:vertical:hover { background: #585b70; }
|
||||
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0; }
|
||||
QScrollBar:horizontal { background: #181825; height: 10px; border: none; }
|
||||
QScrollBar::handle:horizontal { background: #45475a; min-width: 20px; border-radius: 4px; }
|
||||
QScrollBar::handle:horizontal:hover { background: #585b70; }
|
||||
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0; }
|
||||
QToolTip { background-color: #313244; color: #cdd6f4; border: 1px solid #45475a; padding: 4px; }
|
||||
QCheckBox { color: #cdd6f4; }
|
||||
QCheckBox::indicator { width: 14px; height: 14px; }
|
||||
QCheckBox::indicator:unchecked { background: #181825; border: 1px solid #45475a; border-radius: 2px; }
|
||||
QCheckBox::indicator:checked { background: #89b4fa; border: 1px solid #89b4fa; border-radius: 2px; }
|
||||
QDialog { background-color: #1e1e2e; }
|
||||
QFileDialog { background-color: #1e1e2e; }
|
||||
QMessageBox { background-color: #1e1e2e; }
|
||||
QMessageBox QLabel { color: #cdd6f4; }
|
||||
QToolButton { background-color: #313244; color: #cdd6f4; border: 1px solid #45475a; padding: 3px 6px; border-radius: 3px; }
|
||||
QToolButton:hover { background-color: #45475a; }
|
||||
"""
|
||||
|
||||
LIGHT_QSS = """
|
||||
QMainWindow, QWidget { background-color: #f5f5f5; color: #1a1a2e; }
|
||||
QTabWidget::pane { border: 1px solid #ddd; background: #fff; }
|
||||
QTabBar::tab { background: #e8e8e8; color: #555; padding: 6px 14px; border: 1px solid #ddd; border-bottom: none; }
|
||||
QTabBar::tab:selected { background: #fff; color: #1976d2; border-bottom: 2px solid #1976d2; }
|
||||
QListWidget { background-color: #fff; border: 1px solid #ddd; alternate-background-color: #f9f9f9; }
|
||||
QListWidget::item:selected { background-color: #e3f2fd; color: #1976d2; }
|
||||
QPushButton { background-color: #fff; color: #333; border: 1px solid #ccc; padding: 5px 12px; border-radius: 3px; }
|
||||
QPushButton:hover { background-color: #f0f0f0; border-color: #1976d2; }
|
||||
QLineEdit, QTextEdit, QPlainTextEdit, QComboBox, QSpinBox { background-color: #fff; color: #333; border: 1px solid #ccc; padding: 3px 6px; border-radius: 2px; }
|
||||
QLineEdit:focus, QTextEdit:focus { border-color: #1976d2; }
|
||||
QTableWidget { background-color: #fff; gridline-color: #e0e0e0; alternate-background-color: #f9f9f9; }
|
||||
QTableWidget::item:selected { background-color: #e3f2fd; }
|
||||
QHeaderView::section { background-color: #f0f0f0; color: #555; border: none; padding: 4px; border-right: 1px solid #ddd; }
|
||||
QGroupBox { border: 1px solid #ddd; border-radius: 4px; margin-top: 8px; padding-top: 8px; color: #1976d2; font-weight: bold; }
|
||||
QProgressBar { background-color: #f0f0f0; border: 1px solid #ddd; border-radius: 3px; text-align: center; }
|
||||
QProgressBar::chunk { background-color: #1976d2; border-radius: 2px; }
|
||||
QStatusBar { background-color: #f0f0f0; color: #666; }
|
||||
"""
|
||||
|
||||
|
||||
def load_settings() -> dict:
|
||||
if SETTINGS_FILE.exists():
|
||||
try:
|
||||
with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
pass
|
||||
return {"theme": "light"}
|
||||
|
||||
|
||||
def save_settings(settings: dict):
|
||||
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(settings, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def get_theme() -> str:
|
||||
return load_settings().get("theme", "light")
|
||||
|
||||
|
||||
def set_theme(theme: str):
|
||||
s = load_settings()
|
||||
s["theme"] = theme
|
||||
save_settings(s)
|
||||
|
||||
|
||||
def get_qss(theme: "str | None" = None) -> str:
|
||||
if theme is None:
|
||||
theme = get_theme()
|
||||
return DARK_QSS if theme == "dark" else LIGHT_QSS
|
||||
Reference in New Issue
Block a user