584 lines
18 KiB
Python
584 lines
18 KiB
Python
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtGui import QColor, QPalette
|
|
from PySide6.QtWidgets import QApplication
|
|
|
|
|
|
ASSET_DIR = Path(__file__).resolve().parent / "assets"
|
|
|
|
|
|
def asset_url(name: str) -> str:
|
|
return ASSET_DIR.joinpath(name).as_posix()
|
|
|
|
|
|
def apply_app_theme(theme: str) -> None:
|
|
app = QApplication.instance()
|
|
if app is None:
|
|
return
|
|
effective = effective_theme(theme)
|
|
app.setStyle("Fusion")
|
|
if effective == "dark":
|
|
app.setPalette(dark_palette())
|
|
app.setStyleSheet(dark_stylesheet())
|
|
else:
|
|
app.setPalette(light_palette())
|
|
app.setStyleSheet(light_stylesheet())
|
|
|
|
|
|
def effective_theme(theme: str) -> str:
|
|
if theme == "dark":
|
|
return "dark"
|
|
if theme == "light":
|
|
return "light"
|
|
return system_theme()
|
|
|
|
|
|
def system_theme() -> str:
|
|
app = QApplication.instance()
|
|
if app is not None:
|
|
try:
|
|
color_scheme = app.styleHints().colorScheme()
|
|
if color_scheme == Qt.ColorScheme.Dark:
|
|
return "dark"
|
|
if color_scheme == Qt.ColorScheme.Light:
|
|
return "light"
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
if sys.platform.startswith("win"):
|
|
try:
|
|
import winreg
|
|
|
|
with winreg.OpenKey(
|
|
winreg.HKEY_CURRENT_USER,
|
|
r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
|
|
) as key:
|
|
apps_use_light_theme, _ = winreg.QueryValueEx(key, "AppsUseLightTheme")
|
|
return "light" if int(apps_use_light_theme) else "dark"
|
|
except OSError:
|
|
pass
|
|
if app is not None:
|
|
return "dark" if app.style().standardPalette().color(QPalette.Window).lightness() < 128 else "light"
|
|
return "light"
|
|
|
|
|
|
def light_palette() -> QPalette:
|
|
palette = QPalette()
|
|
palette.setColor(QPalette.Window, QColor("#f8fafc"))
|
|
palette.setColor(QPalette.WindowText, QColor("#111827"))
|
|
palette.setColor(QPalette.Base, QColor("#ffffff"))
|
|
palette.setColor(QPalette.AlternateBase, QColor("#f1f5f9"))
|
|
palette.setColor(QPalette.ToolTipBase, QColor("#ffffff"))
|
|
palette.setColor(QPalette.ToolTipText, QColor("#111827"))
|
|
palette.setColor(QPalette.Text, QColor("#111827"))
|
|
palette.setColor(QPalette.Button, QColor("#e5e7eb"))
|
|
palette.setColor(QPalette.ButtonText, QColor("#111827"))
|
|
palette.setColor(QPalette.Highlight, QColor("#c084fc"))
|
|
palette.setColor(QPalette.HighlightedText, QColor("#111827"))
|
|
return palette
|
|
|
|
|
|
def dark_palette() -> QPalette:
|
|
palette = QPalette()
|
|
palette.setColor(QPalette.Window, QColor("#1f1f1f"))
|
|
palette.setColor(QPalette.WindowText, QColor("#f8fafc"))
|
|
palette.setColor(QPalette.Base, QColor("#2b2b2b"))
|
|
palette.setColor(QPalette.AlternateBase, QColor("#353535"))
|
|
palette.setColor(QPalette.ToolTipBase, QColor("#111827"))
|
|
palette.setColor(QPalette.ToolTipText, QColor("#f8fafc"))
|
|
palette.setColor(QPalette.Text, QColor("#f8fafc"))
|
|
palette.setColor(QPalette.Button, QColor("#2f2f2f"))
|
|
palette.setColor(QPalette.ButtonText, QColor("#f8fafc"))
|
|
palette.setColor(QPalette.Highlight, QColor("#c084fc"))
|
|
palette.setColor(QPalette.HighlightedText, QColor("#111827"))
|
|
return palette
|
|
|
|
|
|
def light_stylesheet() -> str:
|
|
return """
|
|
QWidget {
|
|
background-color: #f8fafc;
|
|
color: #111827;
|
|
selection-background-color: #93c5fd;
|
|
selection-color: #111827;
|
|
}
|
|
QGraphicsView {
|
|
background-color: #ffffff;
|
|
border: none;
|
|
}
|
|
QMenuBar {
|
|
background-color: #f1f5f9;
|
|
color: #111827;
|
|
}
|
|
QMenuBar::item {
|
|
background: transparent;
|
|
padding: 5px 10px;
|
|
}
|
|
QMenuBar::item:selected {
|
|
background-color: #e2e8f0;
|
|
}
|
|
QMenu {
|
|
background-color: #ffffff;
|
|
color: #111827;
|
|
border: 1px solid #cbd5e1;
|
|
}
|
|
QMenu::item {
|
|
padding: 5px 24px 5px 24px;
|
|
}
|
|
QMenu::item:selected {
|
|
background-color: #dbeafe;
|
|
}
|
|
QPushButton, QToolButton {
|
|
background-color: #e5e7eb;
|
|
color: #111827;
|
|
border: 1px solid #cbd5e1;
|
|
border-radius: 4px;
|
|
padding: 4px 8px;
|
|
}
|
|
QPushButton:hover, QToolButton:hover {
|
|
background-color: #dbeafe;
|
|
}
|
|
QPushButton:disabled, QToolButton:disabled {
|
|
background-color: #f1f5f9;
|
|
color: #94a3b8;
|
|
border-color: #e2e8f0;
|
|
}
|
|
QTreeWidget, QTableWidget, QListWidget, QLineEdit, QComboBox, QSpinBox {
|
|
background-color: #ffffff;
|
|
color: #111827;
|
|
alternate-background-color: #f1f5f9;
|
|
border: 1px solid #cbd5e1;
|
|
border-radius: 4px;
|
|
min-height: 24px;
|
|
selection-background-color: #bfdbfe;
|
|
selection-color: #111827;
|
|
}
|
|
QLineEdit, QComboBox, QSpinBox {
|
|
padding: 2px 8px;
|
|
}
|
|
QComboBox {
|
|
padding-right: 28px;
|
|
}
|
|
QComboBox:hover, QSpinBox:hover, QLineEdit:hover {
|
|
border-color: #94a3b8;
|
|
}
|
|
QComboBox:focus, QSpinBox:focus, QLineEdit:focus {
|
|
border-color: #60a5fa;
|
|
}
|
|
QComboBox::drop-down {
|
|
subcontrol-origin: padding;
|
|
subcontrol-position: top right;
|
|
width: 24px;
|
|
border-left: 1px solid #cbd5e1;
|
|
border-top-right-radius: 4px;
|
|
border-bottom-right-radius: 4px;
|
|
background-color: #f1f5f9;
|
|
}
|
|
QComboBox::drop-down:hover {
|
|
background-color: #e2e8f0;
|
|
}
|
|
QComboBox::down-arrow {
|
|
image: url("__INPUT_DOWN_ARROW__");
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
QSpinBox {
|
|
padding-right: 28px;
|
|
}
|
|
QSpinBox::up-button, QSpinBox::down-button {
|
|
subcontrol-origin: border;
|
|
width: 24px;
|
|
border-left: 1px solid #cbd5e1;
|
|
background-color: #f1f5f9;
|
|
}
|
|
QSpinBox::up-button {
|
|
subcontrol-position: top right;
|
|
border-top-right-radius: 4px;
|
|
}
|
|
QSpinBox::down-button {
|
|
subcontrol-position: bottom right;
|
|
border-bottom-right-radius: 4px;
|
|
}
|
|
QSpinBox::up-button:hover, QSpinBox::down-button:hover {
|
|
background-color: #e2e8f0;
|
|
}
|
|
QSpinBox::up-arrow {
|
|
image: url("__INPUT_UP_ARROW__");
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
QSpinBox::down-arrow {
|
|
image: url("__INPUT_DOWN_ARROW__");
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
QComboBox QAbstractItemView {
|
|
background-color: #ffffff;
|
|
color: #111827;
|
|
border: 1px solid #cbd5e1;
|
|
selection-background-color: #bfdbfe;
|
|
selection-color: #111827;
|
|
}
|
|
QHeaderView::section {
|
|
background-color: #e5e7eb;
|
|
color: #111827;
|
|
border: 1px solid #cbd5e1;
|
|
padding: 4px;
|
|
}
|
|
QSplitter::handle {
|
|
background-color: #e2e8f0;
|
|
}
|
|
QStatusBar {
|
|
background-color: #f1f5f9;
|
|
color: #111827;
|
|
}
|
|
QToolTip {
|
|
background-color: #ffffff;
|
|
color: #111827;
|
|
border: 1px solid #cbd5e1;
|
|
}
|
|
QScrollBar:vertical {
|
|
background: #f1f5f9;
|
|
width: 12px;
|
|
margin: 12px 0 12px 0;
|
|
border: none;
|
|
}
|
|
QScrollBar::handle:vertical {
|
|
background: #94a3b8;
|
|
min-height: 28px;
|
|
margin: 0 4px;
|
|
border-radius: 2px;
|
|
}
|
|
QScrollBar::handle:vertical:hover {
|
|
background: #64748b;
|
|
margin: 0 2px;
|
|
border-radius: 4px;
|
|
}
|
|
QScrollBar::add-page:vertical,
|
|
QScrollBar::sub-page:vertical {
|
|
background: transparent;
|
|
border: none;
|
|
height: 0;
|
|
}
|
|
QScrollBar::sub-line:vertical,
|
|
QScrollBar::add-line:vertical {
|
|
background: #f1f5f9;
|
|
border: none;
|
|
height: 12px;
|
|
subcontrol-origin: margin;
|
|
}
|
|
QScrollBar::sub-line:vertical {
|
|
subcontrol-position: top;
|
|
}
|
|
QScrollBar::add-line:vertical {
|
|
subcontrol-position: bottom;
|
|
}
|
|
QScrollBar::up-arrow:vertical {
|
|
image: url("__UP_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
QScrollBar::down-arrow:vertical {
|
|
image: url("__DOWN_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
QScrollBar:horizontal {
|
|
background: #f1f5f9;
|
|
height: 12px;
|
|
margin: 0 12px 0 12px;
|
|
border: none;
|
|
}
|
|
QScrollBar::handle:horizontal {
|
|
background: #94a3b8;
|
|
min-width: 28px;
|
|
margin: 4px 0;
|
|
border-radius: 2px;
|
|
}
|
|
QScrollBar::handle:horizontal:hover {
|
|
background: #64748b;
|
|
margin: 2px 0;
|
|
border-radius: 4px;
|
|
}
|
|
QScrollBar::add-page:horizontal,
|
|
QScrollBar::sub-page:horizontal {
|
|
background: transparent;
|
|
border: none;
|
|
width: 0;
|
|
}
|
|
QScrollBar::sub-line:horizontal,
|
|
QScrollBar::add-line:horizontal {
|
|
background: #f1f5f9;
|
|
border: none;
|
|
width: 12px;
|
|
subcontrol-origin: margin;
|
|
}
|
|
QScrollBar::sub-line:horizontal {
|
|
subcontrol-position: left;
|
|
}
|
|
QScrollBar::add-line:horizontal {
|
|
subcontrol-position: right;
|
|
}
|
|
QScrollBar::left-arrow:horizontal {
|
|
image: url("__LEFT_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
QScrollBar::right-arrow:horizontal {
|
|
image: url("__RIGHT_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
""".replace("__INPUT_UP_ARROW__", asset_url("input-arrow-up-light.svg")) \
|
|
.replace("__INPUT_DOWN_ARROW__", asset_url("input-arrow-down-light.svg")) \
|
|
.replace("__UP_ARROW__", asset_url("scrollbar-arrow-up-light.svg")) \
|
|
.replace("__DOWN_ARROW__", asset_url("scrollbar-arrow-down-light.svg")) \
|
|
.replace("__LEFT_ARROW__", asset_url("scrollbar-arrow-left-light.svg")) \
|
|
.replace("__RIGHT_ARROW__", asset_url("scrollbar-arrow-right-light.svg"))
|
|
|
|
|
|
def dark_stylesheet() -> str:
|
|
return """
|
|
QWidget {
|
|
background-color: #1f1f1f;
|
|
color: #f8fafc;
|
|
selection-background-color: #3b82f6;
|
|
selection-color: #f8fafc;
|
|
}
|
|
QGraphicsView {
|
|
background-color: #2b2b2b;
|
|
border: none;
|
|
}
|
|
QMenuBar {
|
|
background-color: #242424;
|
|
color: #f8fafc;
|
|
}
|
|
QMenuBar::item {
|
|
background: transparent;
|
|
padding: 5px 10px;
|
|
}
|
|
QMenuBar::item:selected {
|
|
background-color: #343434;
|
|
}
|
|
QMenu {
|
|
background-color: #242424;
|
|
color: #f8fafc;
|
|
border: 1px solid #444444;
|
|
}
|
|
QMenu::item {
|
|
padding: 5px 24px 5px 24px;
|
|
}
|
|
QMenu::item:selected {
|
|
background-color: #3a3a3a;
|
|
}
|
|
QPushButton, QToolButton {
|
|
background-color: #2f2f2f;
|
|
color: #f8fafc;
|
|
border: 1px solid #4b5563;
|
|
border-radius: 4px;
|
|
padding: 4px 8px;
|
|
}
|
|
QPushButton:hover, QToolButton:hover {
|
|
background-color: #3f3f46;
|
|
}
|
|
QPushButton:disabled, QToolButton:disabled {
|
|
background-color: #262626;
|
|
color: #737373;
|
|
border-color: #3a3a3a;
|
|
}
|
|
QTreeWidget, QTableWidget, QListWidget, QLineEdit, QComboBox, QSpinBox {
|
|
background-color: #2b2b2b;
|
|
color: #f8fafc;
|
|
alternate-background-color: #353535;
|
|
border: 1px solid #444444;
|
|
border-radius: 4px;
|
|
min-height: 24px;
|
|
selection-background-color: #3b82f6;
|
|
selection-color: #f8fafc;
|
|
}
|
|
QLineEdit, QComboBox, QSpinBox {
|
|
padding: 2px 8px;
|
|
}
|
|
QComboBox {
|
|
padding-right: 28px;
|
|
}
|
|
QComboBox:hover, QSpinBox:hover, QLineEdit:hover {
|
|
border-color: #737373;
|
|
}
|
|
QComboBox:focus, QSpinBox:focus, QLineEdit:focus {
|
|
border-color: #60a5fa;
|
|
}
|
|
QComboBox::drop-down {
|
|
subcontrol-origin: padding;
|
|
subcontrol-position: top right;
|
|
width: 24px;
|
|
border-left: 1px solid #444444;
|
|
border-top-right-radius: 4px;
|
|
border-bottom-right-radius: 4px;
|
|
background-color: #262626;
|
|
}
|
|
QComboBox::drop-down:hover {
|
|
background-color: #3f3f46;
|
|
}
|
|
QComboBox::down-arrow {
|
|
image: url("__INPUT_DOWN_ARROW__");
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
QSpinBox {
|
|
padding-right: 28px;
|
|
}
|
|
QSpinBox::up-button, QSpinBox::down-button {
|
|
subcontrol-origin: border;
|
|
width: 24px;
|
|
border-left: 1px solid #444444;
|
|
background-color: #262626;
|
|
}
|
|
QSpinBox::up-button {
|
|
subcontrol-position: top right;
|
|
border-top-right-radius: 4px;
|
|
}
|
|
QSpinBox::down-button {
|
|
subcontrol-position: bottom right;
|
|
border-bottom-right-radius: 4px;
|
|
}
|
|
QSpinBox::up-button:hover, QSpinBox::down-button:hover {
|
|
background-color: #3f3f46;
|
|
}
|
|
QSpinBox::up-arrow {
|
|
image: url("__INPUT_UP_ARROW__");
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
QSpinBox::down-arrow {
|
|
image: url("__INPUT_DOWN_ARROW__");
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
QComboBox QAbstractItemView {
|
|
background-color: #2b2b2b;
|
|
color: #f8fafc;
|
|
border: 1px solid #444444;
|
|
selection-background-color: #3b82f6;
|
|
selection-color: #f8fafc;
|
|
}
|
|
QHeaderView::section {
|
|
background-color: #3a3a3a;
|
|
color: #f8fafc;
|
|
border: 1px solid #444444;
|
|
padding: 4px;
|
|
}
|
|
QSplitter::handle {
|
|
background-color: #111827;
|
|
}
|
|
QStatusBar {
|
|
background-color: #1f1f1f;
|
|
color: #f8fafc;
|
|
}
|
|
QToolTip {
|
|
background-color: #111827;
|
|
color: #f8fafc;
|
|
border: 1px solid #4b5563;
|
|
}
|
|
QScrollBar:vertical {
|
|
background: #1f1f1f;
|
|
width: 12px;
|
|
margin: 12px 0 12px 0;
|
|
border: none;
|
|
}
|
|
QScrollBar::handle:vertical {
|
|
background: #737373;
|
|
min-height: 28px;
|
|
margin: 0 4px;
|
|
border-radius: 2px;
|
|
}
|
|
QScrollBar::handle:vertical:hover {
|
|
background: #a3a3a3;
|
|
margin: 0 2px;
|
|
border-radius: 4px;
|
|
}
|
|
QScrollBar::add-page:vertical,
|
|
QScrollBar::sub-page:vertical {
|
|
background: transparent;
|
|
border: none;
|
|
height: 0;
|
|
}
|
|
QScrollBar::sub-line:vertical,
|
|
QScrollBar::add-line:vertical {
|
|
background: #1f1f1f;
|
|
border: none;
|
|
height: 12px;
|
|
subcontrol-origin: margin;
|
|
}
|
|
QScrollBar::sub-line:vertical {
|
|
subcontrol-position: top;
|
|
}
|
|
QScrollBar::add-line:vertical {
|
|
subcontrol-position: bottom;
|
|
}
|
|
QScrollBar::up-arrow:vertical {
|
|
image: url("__UP_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
QScrollBar::down-arrow:vertical {
|
|
image: url("__DOWN_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
QScrollBar:horizontal {
|
|
background: #1f1f1f;
|
|
height: 12px;
|
|
margin: 0 12px 0 12px;
|
|
border: none;
|
|
}
|
|
QScrollBar::handle:horizontal {
|
|
background: #737373;
|
|
min-width: 28px;
|
|
margin: 4px 0;
|
|
border-radius: 2px;
|
|
}
|
|
QScrollBar::handle:horizontal:hover {
|
|
background: #a3a3a3;
|
|
margin: 2px 0;
|
|
border-radius: 4px;
|
|
}
|
|
QScrollBar::add-page:horizontal,
|
|
QScrollBar::sub-page:horizontal {
|
|
background: transparent;
|
|
border: none;
|
|
width: 0;
|
|
}
|
|
QScrollBar::sub-line:horizontal,
|
|
QScrollBar::add-line:horizontal {
|
|
background: #1f1f1f;
|
|
border: none;
|
|
width: 12px;
|
|
subcontrol-origin: margin;
|
|
}
|
|
QScrollBar::sub-line:horizontal {
|
|
subcontrol-position: left;
|
|
}
|
|
QScrollBar::add-line:horizontal {
|
|
subcontrol-position: right;
|
|
}
|
|
QScrollBar::left-arrow:horizontal {
|
|
image: url("__LEFT_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
QScrollBar::right-arrow:horizontal {
|
|
image: url("__RIGHT_ARROW__");
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
""".replace("__INPUT_UP_ARROW__", asset_url("input-arrow-up-dark.svg")) \
|
|
.replace("__INPUT_DOWN_ARROW__", asset_url("input-arrow-down-dark.svg")) \
|
|
.replace("__UP_ARROW__", asset_url("scrollbar-arrow-up-dark.svg")) \
|
|
.replace("__DOWN_ARROW__", asset_url("scrollbar-arrow-down-dark.svg")) \
|
|
.replace("__LEFT_ARROW__", asset_url("scrollbar-arrow-left-dark.svg")) \
|
|
.replace("__RIGHT_ARROW__", asset_url("scrollbar-arrow-right-dark.svg"))
|