You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Curriculum_Design/src/ui/theme_manager.py

838 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
主题管理器 - 负责检测系统主题并管理应用主题切换
"""
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtCore import QObject, pyqtSignal
import platform
import subprocess
import os
class ThemeManager(QObject):
"""主题管理器类"""
# 主题切换信号
theme_changed = pyqtSignal(bool) # True表示深色模式False表示浅色模式
def __init__(self):
super().__init__()
self._is_dark_theme = False
self._auto_detect = True
self._detection_timer = None
def is_dark_theme(self):
"""检测是否为深色主题"""
if self._auto_detect:
return self._detect_system_theme()
return self._is_dark_theme
def _detect_system_theme(self):
"""检测系统主题"""
try:
# macOS系统主题检测
if platform.system() == 'Darwin':
return self._detect_macos_theme()
# Windows系统主题检测
elif platform.system() == 'Windows':
return self._detect_windows_theme()
# Linux系统主题检测
else:
return self._detect_linux_theme()
except Exception as e:
print(f"主题检测失败: {e}")
return False
def _detect_macos_theme(self):
"""检测macOS系统主题"""
try:
# 使用osascript命令检测macOS主题
result = subprocess.run([
'osascript', '-e',
'tell application "System Events" to tell appearance preferences to return dark mode'
], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
return 'true' in result.stdout.lower()
# 备用方法:检测系统设置
result = subprocess.run([
'defaults', 'read', '-g', 'AppleInterfaceStyle'
], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
return 'dark' in result.stdout.lower()
except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
pass
# 使用Qt的调色板检测作为备用方法
return self._detect_by_palette()
def _detect_windows_theme(self):
"""检测Windows系统主题"""
try:
# Windows 10/11 注册表检测
import winreg
key_path = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize'
value_name = 'AppsUseLightTheme'
try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path) as key:
value, _ = winreg.QueryValueEx(key, value_name)
return value == 0 # 0表示深色模式
except (OSError, FileNotFoundError):
pass
except ImportError:
pass
# 使用Qt的调色板检测作为备用方法
return self._detect_by_palette()
def _detect_linux_theme(self):
"""检测Linux系统主题"""
try:
# 检测GTK主题
result = subprocess.run([
'gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'
], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
theme_name = result.stdout.strip().strip("'")
return any(dark_name in theme_name.lower() for dark_name in ['dark', 'night'])
# 检测颜色方案
result = subprocess.run([
'gsettings', 'get', 'org.gnome.desktop.interface', 'color-scheme'
], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
return 'dark' in result.stdout.lower()
except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
pass
# 使用Qt的调色板检测作为备用方法
return self._detect_by_palette()
def _detect_by_palette(self):
"""使用Qt调色板检测主题"""
app = QApplication.instance()
if app is None:
return False
palette = app.palette()
# 检测背景色和文本色的亮度
window_color = palette.color(QPalette.Window)
text_color = palette.color(QPalette.WindowText)
# 计算亮度
def get_luminance(color):
return (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255
window_luminance = get_luminance(window_color)
text_luminance = get_luminance(text_color)
# 如果背景比文本暗,则为深色主题
return window_luminance < text_luminance
def get_theme_stylesheet(self, is_dark=None):
"""获取主题样式表"""
if is_dark is None:
is_dark = self.is_dark_theme()
if is_dark:
return self._get_dark_stylesheet()
else:
return self._get_light_stylesheet()
def _get_dark_stylesheet(self):
"""深色主题样式表 - Apple设计风格"""
return """
/* Apple设计风格深色主题样式 */
/* 全局文字颜色和字体 - 使用Apple系统字体 */
QWidget {
color: #f0f0f0;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 13px;
}
/* 主窗口 - Apple深色背景 */
QMainWindow {
background-color: #2c2c2e;
}
/* 菜单栏 - Apple深色风格 */
QMenuBar {
background-color: #2c2c2e;
border: none;
border-bottom: 1px solid #404040;
font-size: 13px;
color: #f0f0f0;
padding: 4px 0;
}
QMenuBar::item {
background-color: transparent;
padding: 6px 12px;
color: #f0f0f0;
border-radius: 4px;
margin: 0 1px;
}
QMenuBar::item:selected {
background-color: #404040;
color: #f0f0f0;
}
QMenuBar::item:pressed {
background-color: #505050;
color: #f0f0f0;
}
/* 菜单 - Apple深色风格 */
QMenu {
background-color: #2c2c2e;
border: 1px solid #404040;
border-radius: 8px;
font-size: 13px;
color: #f0f0f0;
padding: 4px 0;
margin: 2px;
}
QMenu::item {
color: #f0f0f0;
background-color: transparent;
border-radius: 4px;
margin: 0 4px;
padding: 4px 20px;
}
QMenu::item:selected {
background-color: #0a84ff;
color: #ffffff;
}
QMenu::item:pressed {
background-color: #0066cc;
color: #ffffff;
}
QMenu::separator {
height: 1px;
background-color: #404040;
margin: 4px 8px;
}
/* 功能区 */
QFrame {
background-color: #2c2c2e;
border: none;
}
/* 组框 */
QGroupBox {
font-size: 12px;
font-weight: normal;
color: #f0f0f0;
background-color: #2c2c2e;
border: none;
border-radius: 8px;
margin-top: 5px;
padding-top: 5px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #a0a0a0;
}
/* 工具按钮 - Apple深色风格 */
QToolButton {
border: 1px solid transparent;
border-radius: 6px;
background-color: #3a3a3c;
font-size: 13px;
color: #f0f0f0;
padding: 6px 12px;
}
QToolButton:hover {
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QToolButton:pressed {
background-color: #5a5a5c;
border: 1px solid #6a6a6c;
}
QToolButton:checked {
background-color: #0a84ff;
border: 1px solid #0a84ff;
color: #ffffff;
}
/* 切换按钮 */
QToolButton[checkable="true"] {
border: 1px solid #4a4a4c;
border-radius: 6px;
background-color: #3a3a3c;
font-size: 12px;
color: #f0f0f0;
padding: 6px 12px;
}
QToolButton[checkable="true"]:hover {
background-color: #4a4a4c;
}
QToolButton[checkable="true"]:checked {
background-color: #0a84ff;
border: 1px solid #0a84ff;
color: #ffffff;
}
/* 下拉框 - Apple深色风格 */
QComboBox {
background-color: #3a3a3c;
border: 1px solid #4a4a4c;
border-radius: 6px;
color: #f0f0f0;
padding: 4px 8px;
selection-background-color: #0a84ff;
selection-color: #ffffff;
}
QComboBox:hover {
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QComboBox::drop-down {
border: none;
width: 20px;
border-left: 1px solid #4a4a4c;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
QComboBox::down-arrow {
image: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 6px solid #a0a0a0;
margin: 6px;
}
QComboBox QAbstractItemView {
background-color: #2c2c2e;
border: 1px solid #4a4a4c;
color: #f0f0f0;
selection-background-color: #0a84ff;
selection-color: #ffffff;
}
/* 文本编辑区域 - Apple深色风格 */
QTextEdit {
background-color: #1c1c1e;
border: none;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 15px;
color: #f0f0f0;
padding: 32px;
line-height: 1.5;
selection-background-color: #0066cc;
selection-color: #ffffff;
}
/* 状态栏 - Apple深色风格 */
QStatusBar {
background-color: #3a3a3c;
border-top: 1px solid #4a4a4c;
font-size: 12px;
color: #a0a0a0;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
padding: 6px 12px;
}
/* 标签 */
QLabel {
color: #f0f0f0;
background-color: transparent;
}
/* 消息框 - Apple深色风格 */
QMessageBox {
background-color: #2c2c2e;
color: #f0f0f0;
border-radius: 12px;
}
QMessageBox QPushButton {
background-color: #3a3a3c;
color: #f0f0f0;
border: 1px solid #4a4a4c;
border-radius: 6px;
padding: 6px 16px;
min-width: 80px;
}
QMessageBox QPushButton:hover {
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QMessageBox QPushButton:pressed {
background-color: #5a5a5c;
border: 1px solid #6a6a6c;
}
QMessageBox QPushButton:default {
background-color: #0a84ff;
color: #ffffff;
border: 1px solid #0a84ff;
}
QMessageBox QPushButton:default:hover {
background-color: #0066cc;
border: 1px solid #0066cc;
}
QMessageBox QPushButton:default:pressed {
background-color: #004d99;
border: 1px solid #004d99;
}
/* 滚动条 - Apple深色风格 */
QScrollBar:vertical {
background-color: transparent;
width: 8px;
border: none;
}
QScrollBar::handle:vertical {
background-color: #5a5a5c;
border-radius: 4px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover {
background-color: #6a6a6c;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
/* 按钮 - Apple深色风格 */
QPushButton {
background-color: #3a3a3c;
color: #f0f0f0;
border: 1px solid #4a4a4c;
border-radius: 6px;
padding: 6px 16px;
font-size: 13px;
}
QPushButton:hover {
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QPushButton:pressed {
background-color: #5a5a5c;
border: 1px solid #6a6a6c;
}
QPushButton:default {
background-color: #0a84ff;
color: #ffffff;
border: 1px solid #0a84ff;
}
QPushButton:default:hover {
background-color: #0066cc;
border: 1px solid #0066cc;
}
QPushButton:default:pressed {
background-color: #004d99;
border: 1px solid #004d99;
}
"""
def _get_light_stylesheet(self):
"""浅色主题样式表 - Apple设计风格"""
return """
/* Apple设计风格浅色主题样式 */
/* 全局文字颜色和字体 - 使用Apple系统字体 */
QWidget {
color: #333333;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 13px;
}
/* 主窗口 - 纯净白色背景 */
QMainWindow {
background-color: #ffffff;
}
/* 菜单栏 - Apple风格 */
QMenuBar {
background-color: #ffffff;
border: none;
border-bottom: 1px solid #e0e0e0;
font-size: 13px;
color: #333333;
padding: 4px 0;
}
QMenuBar::item {
background-color: transparent;
padding: 6px 12px;
color: #333333;
border-radius: 4px;
margin: 0 1px;
}
QMenuBar::item:selected {
background-color: #f0f0f0;
color: #333333;
}
QMenuBar::item:pressed {
background-color: #e0e0e0;
color: #333333;
}
/* 菜单 - Apple风格 */
QMenu {
background-color: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 8px;
font-size: 13px;
color: #333333;
padding: 4px 0;
margin: 2px;
}
QMenu::item {
color: #333333;
background-color: transparent;
border-radius: 4px;
margin: 0 4px;
padding: 4px 20px;
}
QMenu::item:selected {
background-color: #007aff;
color: #ffffff;
}
QMenu::item:pressed {
background-color: #0062cc;
color: #ffffff;
}
QMenu::separator {
height: 1px;
background-color: #e0e0e0;
margin: 4px 8px;
}
/* 功能区 */
QFrame {
background-color: #ffffff;
border: none;
}
/* 组框 */
QGroupBox {
font-size: 12px;
font-weight: normal;
color: #333333;
background-color: #ffffff;
border: none;
border-radius: 8px;
margin-top: 5px;
padding-top: 5px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #666666;
}
/* 工具按钮 - Apple风格 */
QToolButton {
border: 1px solid transparent;
border-radius: 6px;
background-color: #f6f6f6;
font-size: 13px;
color: #333333;
padding: 6px 12px;
}
QToolButton:hover {
background-color: #e0e0e0;
border: 1px solid #d0d0d0;
}
QToolButton:pressed {
background-color: #d0d0d0;
border: 1px solid #c0c0c0;
}
QToolButton:checked {
background-color: #007aff;
border: 1px solid #007aff;
color: #ffffff;
}
/* 切换按钮 */
QToolButton[checkable="true"] {
border: 1px solid #d0d0d0;
border-radius: 6px;
background-color: #f6f6f6;
font-size: 12px;
color: #333333;
padding: 6px 12px;
}
QToolButton[checkable="true"]:hover {
background-color: #e0e0e0;
}
QToolButton[checkable="true"]:checked {
background-color: #007aff;
border: 1px solid #007aff;
color: #ffffff;
}
/* 下拉框 - Apple风格 */
QComboBox {
background-color: #f6f6f6;
border: 1px solid #d0d0d0;
border-radius: 6px;
color: #333333;
padding: 4px 8px;
selection-background-color: #007aff;
selection-color: #ffffff;
}
QComboBox:hover {
background-color: #e0e0e0;
border: 1px solid #c0c0c0;
}
QComboBox::drop-down {
border: none;
width: 20px;
border-left: 1px solid #d0d0d0;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
QComboBox::down-arrow {
image: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 6px solid #666666;
margin: 6px;
}
QComboBox QAbstractItemView {
border: 1px solid #d0d0d0;
color: #333333;
background-color: #ffffff;
selection-background-color: #007aff;
selection-color: #ffffff;
}
/* 文本编辑区域 - Apple风格 */
QTextEdit {
background-color: #ffffff;
border: none;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 15px;
color: #333333;
padding: 32px;
line-height: 1.5;
selection-background-color: #b3d9ff;
selection-color: #333333;
}
/* 状态栏 - Apple风格 */
QStatusBar {
background-color: #f6f6f6;
border-top: 1px solid #e0e0e0;
font-size: 12px;
color: #666666;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
padding: 6px 12px;
}
/* 标签 */
QLabel {
color: #333333;
background-color: transparent;
}
/* 消息框 - Apple风格 */
QMessageBox {
background-color: #ffffff;
color: #333333;
border-radius: 12px;
}
QMessageBox QPushButton {
background-color: #f6f6f6;
color: #333333;
border: 1px solid #d0d0d0;
border-radius: 6px;
padding: 6px 16px;
min-width: 80px;
}
QMessageBox QPushButton:hover {
background-color: #e0e0e0;
border: 1px solid #c0c0c0;
}
QMessageBox QPushButton:pressed {
background-color: #d0d0d0;
border: 1px solid #a0a0a0;
}
QMessageBox QPushButton:default {
background-color: #007aff;
color: #ffffff;
border: 1px solid #007aff;
}
QMessageBox QPushButton:default:hover {
background-color: #0062cc;
border: 1px solid #0062cc;
}
QMessageBox QPushButton:default:pressed {
background-color: #004a99;
border: 1px solid #004a99;
}
/* 滚动条 - Apple风格 */
QScrollBar:vertical {
background-color: transparent;
width: 8px;
border: none;
}
QScrollBar::handle:vertical {
background-color: #c0c0c0;
border-radius: 4px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover {
background-color: #a0a0a0;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
/* 按钮 - Apple风格 */
QPushButton {
background-color: #f6f6f6;
color: #333333;
border: 1px solid #d0d0d0;
border-radius: 6px;
padding: 6px 16px;
font-size: 13px;
}
QPushButton:hover {
background-color: #e0e0e0;
border: 1px solid #c0c0c0;
}
QPushButton:pressed {
background-color: #d0d0d0;
border: 1px solid #a0a0a0;
}
QPushButton:default {
background-color: #007aff;
color: #ffffff;
border: 1px solid #007aff;
}
QPushButton:default:hover {
background-color: #0062cc;
border: 1px solid #0062cc;
}
QPushButton:default:pressed {
background-color: #004a99;
border: 1px solid #004a99;
}
"""
def set_dark_theme(self, is_dark):
"""设置深色主题"""
self._is_dark_theme = is_dark
self._auto_detect = False
self.theme_changed.emit(is_dark)
def enable_auto_detection(self, enabled=True):
"""启用/禁用自动主题检测"""
self._auto_detect = enabled
if enabled:
self.theme_changed.emit(self.is_dark_theme())
def get_current_theme_colors(self):
"""获取当前主题颜色配置"""
is_dark = self.is_dark_theme()
if is_dark:
return {
'background': '#1e1e1e',
'surface': '#2d2d2d',
'surface_hover': '#3c3c3c',
'text': '#e0e0e0',
'text_secondary': '#b0b0b0',
'border': '#3c3c3c',
'accent': '#0078d4',
'accent_hover': '#106ebe'
}
else:
return {
'background': '#f3f2f1',
'surface': '#ffffff',
'surface_hover': '#f0f0f0',
'text': '#333333',
'text_secondary': '#666666',
'border': '#d0d0d0',
'accent': '#0078d7',
'accent_hover': '#005a9e'
}
# 全局主题管理器实例
theme_manager = ThemeManager()