feat(主题): 添加主题管理器并实现深色/浅色模式切换

实现主题管理器功能,支持自动检测系统主题并提供深色/浅色模式切换
为功能区、菜单栏、文本编辑器等组件添加主题样式支持
在视图菜单中添加模式切换选项,允许用户手动选择主题
马子昂 4 months ago
parent fcc7bfd784
commit 5180135447

@ -0,0 +1,665 @@
#!/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):
"""深色主题样式表"""
return """
/* 深色主题样式 */
/* 全局文字颜色 */
QWidget {
color: #e0e0e0;
}
/* 主窗口 */
QMainWindow {
background-color: #1e1e1e;
}
/* 菜单栏 */
QMenuBar {
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
font-size: 12px;
color: #e0e0e0;
}
QMenuBar::item {
background-color: transparent;
padding: 4px 10px;
color: #e0e0e0;
}
QMenuBar::item:selected {
background-color: #3c3c3c;
}
/* 菜单 */
QMenu {
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
font-size: 12px;
color: #e0e0e0;
}
QMenu::item {
padding: 4px 20px;
color: #e0e0e0;
}
QMenu::item:selected {
background-color: #3c3c3c;
}
/* 功能区 */
QFrame {
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
}
/* 组框 */
QGroupBox {
font-size: 11px;
font-weight: normal;
color: #e0e0e0;
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
border-radius: 0px;
margin-top: 5px;
padding-top: 5px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #e0e0e0;
}
/* 按钮 */
QToolButton {
border: 1px solid #3c3c3c;
border-radius: 3px;
background-color: #3c3c3c;
font-size: 11px;
color: #e0e0e0;
padding: 3px 6px;
}
QToolButton:hover {
background-color: #4a4a4a;
border: 1px solid #5a5a5a;
}
QToolButton:pressed {
background-color: #2a2a2a;
border: 1px solid #1a1a1a;
}
QToolButton:checked {
background-color: #0078d4;
border: 1px solid #106ebe;
}
/* 切换按钮 */
QToolButton[checkable="true"] {
border: 1px solid #3c3c3c;
border-radius: 2px;
background-color: #3c3c3c;
font-size: 12px;
font-weight: bold;
color: #e0e0e0;
}
QToolButton[checkable="true"]:hover {
background-color: #4a4a4a;
}
QToolButton[checkable="true"]:checked {
background-color: #0078d4;
border: 1px solid #106ebe;
}
/* 下拉框 - 修复文字不可见问题 */
QComboBox {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
border-radius: 2px;
color: #e0e0e0;
padding: 2px 5px;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
}
QComboBox:hover {
background-color: #4a4a4a;
border: 1px solid #6a6a6a;
}
QComboBox::drop-down {
border: none;
width: 15px;
background-color: #3c3c3c;
}
QComboBox::down-arrow {
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid #e0e0e0;
}
/* 下拉框弹出列表 */
QComboBox QAbstractItemView {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
color: #e0e0e0;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
}
/* 字体下拉框特殊处理 */
QFontComboBox {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
border-radius: 2px;
color: #e0e0e0;
padding: 2px 5px;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
}
QFontComboBox:hover {
background-color: #4a4a4a;
border: 1px solid #6a6a6a;
}
QFontComboBox QAbstractItemView {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
color: #e0e0e0;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
}
/* 文本编辑器 */
QTextEdit {
background-color: #1e1e1e;
border: 1px solid #3c3c3c;
color: #e0e0e0;
padding: 20px;
line-height: 1.5;
}
/* 状态栏 */
QStatusBar {
background-color: #2d2d2d;
border-top: 1px solid #3c3c3c;
font-size: 11px;
color: #e0e0e0;
}
/* 标签 */
QLabel {
color: #e0e0e0;
background-color: transparent;
}
/* 滚动条 */
QScrollBar:vertical {
background-color: #2d2d2d;
width: 12px;
border: none;
}
QScrollBar::handle:vertical {
background-color: #5a5a5a;
border-radius: 6px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover {
background-color: #6a6a6a;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
"""
def _get_light_stylesheet(self):
"""浅色主题样式表 - 白底黑字"""
return """
/* 浅色主题样式 - 白底黑字 */
/* 全局文字颜色 */
QWidget {
color: #333333;
}
/* 主窗口 */
QMainWindow {
background-color: #f3f2f1;
}
/* 菜单栏 */
QMenuBar {
background-color: #ffffff;
border: 1px solid #d0d0d0;
font-size: 12px;
color: #333333;
}
QMenuBar::item {
background-color: transparent;
padding: 4px 10px;
color: #333333;
}
QMenuBar::item:selected {
background-color: #f0f0f0;
}
/* 菜单 */
QMenu {
background-color: #ffffff;
border: 1px solid #d0d0d0;
font-size: 12px;
color: #333333;
}
QMenu::item {
padding: 4px 20px;
color: #333333;
}
QMenu::item:selected {
background-color: #f0f0f0;
}
/* 功能区 */
QFrame {
background-color: #ffffff;
border: 1px solid #d0d0d0;
}
/* 组框 */
QGroupBox {
font-size: 11px;
font-weight: normal;
color: #333333;
background-color: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 0px;
margin-top: 5px;
padding-top: 5px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #333333;
}
/* 按钮 */
QToolButton {
border: 1px solid #d0d0d0;
border-radius: 3px;
background-color: #ffffff;
font-size: 11px;
color: #333333;
padding: 3px 6px;
}
QToolButton:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
}
QToolButton:pressed {
background-color: #e0e0e0;
border: 1px solid #005a9e;
}
QToolButton:checked {
background-color: #0078d7;
border: 1px solid #005a9e;
color: #ffffff;
}
/* 切换按钮 */
QToolButton[checkable="true"] {
border: 1px solid #d0d0d0;
border-radius: 2px;
background-color: #ffffff;
font-size: 12px;
font-weight: bold;
color: #333333;
}
QToolButton[checkable="true"]:hover {
background-color: #f0f0f0;
}
QToolButton[checkable="true"]:checked {
background-color: #0078d7;
border: 1px solid #005a9e;
color: #ffffff;
}
/* 下拉框 - 白底黑字 */
QComboBox {
background-color: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 2px;
color: #333333;
padding: 2px 5px;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
QComboBox:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
}
QComboBox::drop-down {
border: none;
width: 15px;
background-color: #ffffff;
}
QComboBox::down-arrow {
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid #333333;
}
/* 下拉框弹出列表 */
QComboBox QAbstractItemView {
background-color: #ffffff;
border: 1px solid #d0d0d0;
color: #333333;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
/* 字体下拉框特殊处理 - 白底黑字 */
QFontComboBox {
background-color: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 2px;
color: #333333;
padding: 2px 5px;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
QFontComboBox:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
}
QFontComboBox::drop-down {
border: none;
width: 15px;
background-color: #ffffff;
}
QFontComboBox::down-arrow {
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid #333333;
}
QFontComboBox QAbstractItemView {
background-color: #ffffff;
border: 1px solid #d0d0d0;
color: #333333;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
/* 文本编辑器 */
QTextEdit {
background-color: #ffffff;
border: 1px solid #d0d0d0;
color: #000000;
padding: 20px;
line-height: 1.5;
}
/* 状态栏 */
QStatusBar {
background-color: #ffffff;
border-top: 1px solid #d0d0d0;
font-size: 11px;
color: #333333;
}
/* 标签 */
QLabel {
color: #333333;
background-color: transparent;
}
/* 滚动条 */
QScrollBar:vertical {
background-color: #ffffff;
width: 12px;
border: none;
}
QScrollBar::handle:vertical {
background-color: #c0c0c0;
border-radius: 6px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover {
background-color: #a0a0a0;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
"""
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()

@ -10,6 +10,9 @@ import requests
import json
from datetime import datetime
# 导入主题管理器
from .theme_manager import theme_manager
class WordRibbonTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
@ -33,6 +36,9 @@ class WordRibbon(QFrame):
self.quote_group = None # 每日一言组件组
self.ribbon_layout = None # 功能区布局
self.setup_ui()
# 初始化主题
self.init_theme()
def setup_ui(self):
self.setFrameStyle(QFrame.NoFrame)
@ -80,7 +86,7 @@ class WordRibbon(QFrame):
self.font_combo.currentFontChanged.connect(self.on_font_changed)
self.font_size_combo = QComboBox()
self.font_size_combo.addItems(['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'])
self.font_size_combo.setFixedWidth(50)
self.font_size_combo.setFixedWidth(70)
self.font_size_combo.setCurrentText('12')
self.font_size_combo.currentTextChanged.connect(self.on_font_size_changed)
@ -114,6 +120,7 @@ class WordRibbon(QFrame):
# 段落组
paragraph_group = self.create_ribbon_group("段落")
paragraph_group.setFixedWidth(320) # 增加宽度以适应更宽的按钮
# 对齐方式
align_layout = QHBoxLayout()
@ -142,13 +149,24 @@ class WordRibbon(QFrame):
# 编辑组
editing_group = self.create_ribbon_group("编辑")
self.find_btn = self.create_ribbon_button("查找", "Ctrl+F", "find")
self.replace_btn = self.create_ribbon_button("替换", "Ctrl+H", "replace")
editing_layout = QVBoxLayout()
# 创建查找替换按钮,使用更适合水平排列的样式
self.find_btn = QToolButton()
self.find_btn.setText("查找")
self.find_btn.setToolButtonStyle(Qt.ToolButtonTextOnly)
self.find_btn.setFixedSize(50, 25) # 设置适合水平排列的尺寸
self.replace_btn = QToolButton()
self.replace_btn.setText("替换")
self.replace_btn.setToolButtonStyle(Qt.ToolButtonTextOnly)
self.replace_btn.setFixedSize(50, 25) # 设置适合水平排列的尺寸
editing_layout = QHBoxLayout() # 改为水平布局
editing_layout.addWidget(self.find_btn)
editing_layout.addWidget(self.replace_btn)
editing_layout.addStretch() # 添加弹性空间
editing_group.setLayout(editing_layout)
editing_group.setFixedWidth(120) # 设置编辑组宽度以适应查找替换按钮
layout.addWidget(editing_group)
layout.addStretch()
@ -177,6 +195,275 @@ class WordRibbon(QFrame):
"""字体颜色按钮点击处理"""
pass
def init_theme(self):
"""初始化主题"""
# 连接主题切换信号
theme_manager.theme_changed.connect(self.on_theme_changed)
# 应用当前主题
self.apply_theme()
def apply_theme(self):
"""应用主题样式"""
is_dark = theme_manager.is_dark_theme()
# 更新功能区背景
colors = theme_manager.get_current_theme_colors()
self.ribbon_area.setStyleSheet(f"""
QFrame {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-top: none;
}}
QGroupBox {{
background-color: {colors['surface']};
color: {colors['text']};
border: 1px solid {colors['border']};
}}
QGroupBox::title {{
color: {colors['text']};
background-color: transparent;
}}
QLabel {{
color: {colors['text']};
background-color: transparent;
}}
""")
# 更新下拉框样式
self.update_combo_styles(is_dark)
def update_combo_styles(self, is_dark):
"""更新下拉框样式"""
colors = theme_manager.get_current_theme_colors()
# 字体下拉框样式
if hasattr(self, 'font_combo'):
self.font_combo.setStyleSheet(f"""
QFontComboBox {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 2px;
color: {colors['text']};
padding: 2px 5px;
}}
QFontComboBox:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QFontComboBox::drop-down {{
border: none;
width: 15px;
}}
QFontComboBox::down-arrow {{
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid {colors['text']};
}}
""")
# 字体大小下拉框样式
if hasattr(self, 'font_size_combo'):
self.font_size_combo.setStyleSheet(f"""
QComboBox {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 2px;
color: {colors['text']};
padding: 2px 5px;
}}
QComboBox:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QComboBox::drop-down {{
border: none;
width: 15px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid {colors['text']};
}}
""")
# 更新字体工具栏按钮样式
self.update_font_button_styles(is_dark)
def update_font_button_styles(self, is_dark):
"""更新字体工具栏按钮样式"""
colors = theme_manager.get_current_theme_colors()
# 字体样式按钮样式 (B, I, U)
font_button_style = f"""
QToolButton {{
border: 1px solid {colors['border']};
border-radius: 2px;
background-color: {colors['surface']};
font-size: 12px;
font-weight: bold;
color: {colors['text']};
}}
QToolButton:hover {{
background-color: {colors['surface_hover']};
}}
QToolButton:checked {{
background-color: {colors['accent']};
border: 1px solid {colors['accent']};
color: {colors['surface']};
}}
"""
# 应用到字体样式按钮
for btn_name in ['bold_btn', 'italic_btn', 'underline_btn']:
if hasattr(self, btn_name):
getattr(self, btn_name).setStyleSheet(font_button_style)
# 颜色按钮样式 (A)
color_button_style = f"""
QToolButton {{
border: 1px solid {colors['border']};
border-radius: 2px;
background-color: {colors['surface']};
font-size: 12px;
font-weight: bold;
color: {colors['text']};
}}
QToolButton:hover {{
background-color: {colors['surface_hover']};
}}
QToolButton:pressed {{
background-color: {colors['accent']};
border: 1px solid {colors['accent']};
color: {colors['surface']};
}}
"""
# 应用到颜色按钮
if hasattr(self, 'color_btn'):
self.color_btn.setStyleSheet(color_button_style)
# 段落对齐按钮样式 (左对齐、居中、右对齐、两端对齐)
paragraph_button_style = f"""
QToolButton {{
border: 1px solid {colors['border']};
border-radius: 2px;
background-color: {colors['surface']};
font-size: 12px;
font-weight: bold;
color: {colors['text']};
}}
QToolButton:hover {{
background-color: {colors['surface_hover']};
}}
QToolButton:checked {{
background-color: {colors['accent']};
border: 1px solid {colors['accent']};
color: {colors['surface']};
}}
"""
# 应用到段落对齐按钮
for btn_name in ['align_left_btn', 'align_center_btn', 'align_right_btn', 'align_justify_btn']:
if hasattr(self, btn_name):
getattr(self, btn_name).setStyleSheet(paragraph_button_style)
# 编辑按钮样式 (查找、替换)
edit_button_style = f"""
QToolButton {{
border: 1px solid {colors['border']};
border-radius: 3px;
background-color: {colors['surface']};
font-size: 11px;
color: {colors['text']};
padding: 3px 6px;
}}
QToolButton:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QToolButton:pressed {{
background-color: {colors['accent']};
border: 1px solid {colors['accent']};
color: {colors['surface']};
}}
"""
# 应用到编辑按钮
for btn_name in ['find_btn', 'replace_btn']:
if hasattr(self, btn_name):
getattr(self, btn_name).setStyleSheet(edit_button_style)
# 城市选择下拉框样式
if hasattr(self, 'city_combo'):
self.city_combo.setStyleSheet(f"""
QComboBox {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 2px;
color: {colors['text']};
padding: 2px 5px;
}}
QComboBox:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QComboBox::drop-down {{
border: none;
width: 15px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid {colors['text']};
}}
""")
# 每日一言类型下拉框样式
if hasattr(self, 'quote_type_combo'):
self.quote_type_combo.setStyleSheet(f"""
QComboBox {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 2px;
color: {colors['text']};
padding: 2px 5px;
}}
QComboBox:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QComboBox::drop-down {{
border: none;
width: 15px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid {colors['text']};
}}
""")
def on_theme_changed(self, is_dark):
"""主题切换槽函数"""
self.apply_theme()
def create_weather_group(self):
"""创建天气组件组"""
if self.weather_group is not None:
@ -423,7 +710,13 @@ class WordRibbon(QFrame):
btn.setText(text)
btn.setCheckable(True)
btn.setToolButtonStyle(Qt.ToolButtonTextOnly)
btn.setFixedSize(30, 25)
# 根据文本长度设置宽度,中文字符需要更宽
if len(text) <= 1:
btn.setFixedSize(30, 25) # 单个字符如B、I、U
elif len(text) <= 2:
btn.setFixedSize(45, 25) # 两个字符(如"居中"
else:
btn.setFixedSize(60, 25) # 三个字符及以上(如"左对齐"、"两端对齐"
btn.setStyleSheet("""
QToolButton {
border: 1px solid #d0d0d0;

@ -17,6 +17,9 @@ from ui.word_style_ui import WeatherAPI
from file_parser import FileParser
from input_handler.input_processor import InputProcessor
# 导入主题管理器
from ui.theme_manager import theme_manager
class WeatherFetchThread(QThread):
weather_fetched = pyqtSignal(dict)
@ -98,6 +101,9 @@ class WordStyleMainWindow(QMainWindow):
self.setWindowTitle("文档1 - MagicWord")
self.setGeometry(100, 100, 1200, 800)
# 初始化主题
self.init_theme()
# 设置应用程序图标
self.set_window_icon()
@ -120,6 +126,252 @@ class WordStyleMainWindow(QMainWindow):
# 初始化时刷新天气
self.refresh_weather()
def init_theme(self):
"""初始化主题"""
# 连接主题切换信号
theme_manager.theme_changed.connect(self.on_theme_changed)
# 应用当前主题
self.apply_theme()
def apply_theme(self):
"""应用主题样式"""
is_dark = theme_manager.is_dark_theme()
# 应用主题样式表
stylesheet = theme_manager.get_theme_stylesheet(is_dark)
if stylesheet.strip(): # 只在有样式内容时应用
self.setStyleSheet(stylesheet)
# 更新组件样式
self.update_component_styles(is_dark)
def update_component_styles(self, is_dark):
"""更新组件样式"""
colors = theme_manager.get_current_theme_colors()
# 更新菜单栏样式
if hasattr(self, 'menubar'):
self.menubar.setStyleSheet(f"""
QMenuBar {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
font-size: 12px;
color: {colors['text']};
}}
QMenuBar::item {{
background-color: transparent;
padding: 4px 10px;
color: {colors['text']};
}}
QMenuBar::item:selected {{
background-color: {colors['surface_hover']};
}}
QMenuBar::item:pressed {{
background-color: {colors['accent']};
color: {colors['surface']};
}}
""")
# 更新文件菜单样式
if hasattr(self, 'file_menu') and self.file_menu is not None:
self.file_menu.setStyleSheet(f"""
QMenu {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
font-size: 12px;
color: {colors['text']};
}}
QMenu::item {{
padding: 4px 20px;
color: {colors['text']};
background-color: transparent;
}}
QMenu::item:selected {{
background-color: {colors['surface_hover']};
}}
QMenu::item:pressed {{
background-color: {colors['accent']};
color: {colors['surface']};
}}
""")
# 更新视图菜单样式
if hasattr(self, 'view_menu') and self.view_menu is not None:
self.view_menu.setStyleSheet(f"""
QMenu {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
font-size: 12px;
color: {colors['text']};
}}
QMenu::item {{
padding: 4px 20px;
color: {colors['text']};
background-color: transparent;
}}
QMenu::item:selected {{
background-color: {colors['surface_hover']};
}}
QMenu::item:pressed {{
background-color: {colors['accent']};
color: {colors['surface']};
}}
""")
# 更新开始菜单样式
if hasattr(self, 'start_menu') and self.start_menu is not None:
self.start_menu.setStyleSheet(f"""
QMenu {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
font-size: 12px;
color: {colors['text']};
}}
QMenu::item {{
padding: 4px 20px;
color: {colors['text']};
background-color: transparent;
}}
QMenu::item:selected {{
background-color: {colors['surface_hover']};
}}
QMenu::item:pressed {{
background-color: {colors['accent']};
color: {colors['surface']};
}}
""")
# 更新文本编辑器样式
if hasattr(self, 'text_edit'):
self.text_edit.setStyleSheet(f"""
QTextEdit {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 0px;
font-family: 'Calibri', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 12pt;
color: {colors['text']};
padding: 40px;
line-height: 1.5;
}}
""")
# 更新滚动区域样式
if hasattr(self, 'scroll_area'):
self.scroll_area.setStyleSheet(f"""
QScrollArea {{
background-color: {colors['background']};
border: none;
}}
QScrollArea QWidget {{
background-color: {colors['background']};
}}
""")
# 更新图片列表样式(如果已存在)
if hasattr(self, 'image_list_widget') and self.image_list_widget is not None:
self.image_list_widget.setStyleSheet(f"""
QListWidget {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 4px;
font-size: 11px;
color: {colors['text']};
}}
QListWidget::item {{
padding: 5px;
border-bottom: 1px solid {colors['border']};
color: {colors['text']};
}}
QListWidget::item:selected {{
background-color: {colors['accent']};
color: {colors['surface']};
}}
""")
# 更新功能区下拉框样式
if hasattr(self, 'ribbon'):
self.update_ribbon_styles(is_dark)
def update_ribbon_styles(self, is_dark):
"""更新功能区样式"""
colors = theme_manager.get_current_theme_colors()
# 更新字体下拉框样式
if hasattr(self.ribbon, 'font_combo'):
self.ribbon.font_combo.setStyleSheet(f"""
QComboBox {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 2px;
color: {colors['text']};
padding: 2px 5px;
}}
QComboBox:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QComboBox::drop-down {{
border: none;
width: 15px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid {colors['text']};
}}
""")
# 更新字体大小下拉框样式
if hasattr(self.ribbon, 'size_combo'):
self.ribbon.size_combo.setStyleSheet(f"""
QComboBox {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 2px;
color: {colors['text']};
padding: 2px 5px;
}}
QComboBox:hover {{
background-color: {colors['surface_hover']};
border: 1px solid {colors['accent']};
}}
QComboBox::drop-down {{
border: none;
width: 15px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid {colors['text']};
}}
""")
def on_theme_changed(self, is_dark):
"""主题切换槽函数"""
self.apply_theme()
def set_window_icon(self):
"""设置窗口图标"""
# 使用我们创建的Word风格图标
@ -248,30 +500,11 @@ class WordStyleMainWindow(QMainWindow):
"""创建菜单栏"""
menubar = self.menuBar()
menubar.setNativeMenuBar(False)
menubar.setStyleSheet("""
QMenuBar {
background-color: #f3f2f1;
border-bottom: 1px solid #d0d0d0;
font-size: 12px;
color: #333333;
}
QMenuBar::item:selected {
background-color: #e1e1e1;
}
""")
self.menubar = menubar # 保存为实例变量以便后续样式更新
# 文件菜单
file_menu = menubar.addMenu('文件(F)')
file_menu.setStyleSheet("""
QMenu {
background-color: #ffffff;
border: 1px solid #d0d0d0;
font-size: 12px;
}
QMenu::item:selected {
background-color: #e1e1e1;
}
""")
self.file_menu = file_menu # 保存为实例变量
# 新建
new_action = QAction('新建(N)', self)
@ -305,6 +538,7 @@ class WordStyleMainWindow(QMainWindow):
# 开始菜单
start_menu = menubar.addMenu('开始(S)')
self.start_menu = start_menu # 保存为实例变量
# 撤销
undo_action = QAction('撤销(U)', self)
@ -340,6 +574,7 @@ class WordStyleMainWindow(QMainWindow):
# 视图菜单
view_menu = menubar.addMenu('视图(V)')
self.view_menu = view_menu # 保存为实例变量
# 阅读视图
read_view_action = QAction('阅读视图', self)
@ -355,6 +590,25 @@ class WordStyleMainWindow(QMainWindow):
view_menu.addSeparator()
# 模式选择子菜单
theme_menu = view_menu.addMenu('模式')
# 白色模式
self.light_mode_action = QAction('白色模式', self)
self.light_mode_action.setCheckable(True)
self.light_mode_action.setChecked(not theme_manager.is_dark_theme()) # 根据当前主题设置
self.light_mode_action.triggered.connect(self.set_light_mode)
theme_menu.addAction(self.light_mode_action)
# 黑色模式
self.dark_mode_action = QAction('黑色模式', self)
self.dark_mode_action.setCheckable(True)
self.dark_mode_action.setChecked(theme_manager.is_dark_theme()) # 根据当前主题设置
self.dark_mode_action.triggered.connect(self.set_dark_mode)
theme_menu.addAction(self.dark_mode_action)
view_menu.addSeparator()
# 视图模式选择
view_mode_menu = view_menu.addMenu('视图模式')
@ -436,11 +690,11 @@ class WordStyleMainWindow(QMainWindow):
# 创建滚动区域
from PyQt5.QtWidgets import QScrollArea
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
scroll_area.setStyleSheet("""
self.scroll_area = QScrollArea() # 保存为实例变量
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll_area.setStyleSheet("""
QScrollArea {
background-color: #e1e1e1;
border: none;
@ -508,8 +762,8 @@ class WordStyleMainWindow(QMainWindow):
document_layout.addWidget(self.image_list_widget)
document_container.setLayout(document_layout)
scroll_area.setWidget(document_container)
main_layout.addWidget(scroll_area)
self.scroll_area.setWidget(document_container)
main_layout.addWidget(self.scroll_area)
def init_network_services(self):
"""初始化网络服务"""
@ -2068,6 +2322,20 @@ class WordStyleMainWindow(QMainWindow):
except Exception as e:
self.status_bar.showMessage(f"显示图片失败: {str(e)}", 3000)
def set_light_mode(self):
"""设置为白色模式"""
theme_manager.set_dark_theme(False)
self.light_mode_action.setChecked(True)
self.dark_mode_action.setChecked(False)
self.status_bar.showMessage("已切换到白色模式", 2000)
def set_dark_mode(self):
"""设置为黑色模式"""
theme_manager.set_dark_theme(True)
self.light_mode_action.setChecked(False)
self.dark_mode_action.setChecked(True)
self.status_bar.showMessage("已切换到黑色模式", 2000)
def show_image_viewer(self, filename, image_data):
"""显示图片查看器"""
try:

Loading…
Cancel
Save