diff --git a/src/ui/theme_manager.py b/src/ui/theme_manager.py new file mode 100644 index 0000000..2b99e10 --- /dev/null +++ b/src/ui/theme_manager.py @@ -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() \ No newline at end of file diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index 065a8de..4e34b64 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -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; diff --git a/src/word_main_window.py b/src/word_main_window.py index 3a18006..fbf8eb3 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -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: