From 75dbeb24e99409aa5c3c4bfa9a49581218ee868c Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 00:41:30 +0800 Subject: [PATCH 1/7] Enhance calendar widget and theme manager with optimized Apple design styles - Updated the `CalendarFloatingWidget` and `CalendarWidget` classes to improve dark and light theme styles, aligning with an optimized Apple design aesthetic. - Adjusted font sizes, button styles, and color schemes for better visual consistency and usability. - Enhanced the `ThemeManager` to provide refined styles for various UI components, including menus, buttons, and toolbars. - Implemented responsive styles for button states (hover, pressed) and improved accessibility with disabled text colors. - Ensured that theme changes propagate correctly across the application, particularly in the `WordStyleMainWindow`. --- src/deepseek_dialog_window.py | 298 +++++++++++++------------ src/ui/calendar_floating_widget.py | 76 ++++--- src/ui/calendar_widget.py | 343 +++++++++++++++++++---------- src/ui/theme_manager.py | 150 +++++++------ src/word_main_window.py | 2 +- 5 files changed, 511 insertions(+), 358 deletions(-) diff --git a/src/deepseek_dialog_window.py b/src/deepseek_dialog_window.py index 22cf9f1..6b6e4d0 100644 --- a/src/deepseek_dialog_window.py +++ b/src/deepseek_dialog_window.py @@ -187,134 +187,109 @@ class DeepSeekDialogWindow(QDialog): self.streaming_finished.connect(self.on_streaming_finished) def toggle_theme(self): - """切换黑白主题""" - if hasattr(self, 'current_theme') and self.current_theme == "dark": - self.apply_theme("light") - else: - self.apply_theme("dark") + """切换黑白主题 - 使用主题管理器""" + try: + from .ui.theme_manager import theme_manager + except ImportError: + # 处理直接运行的情况 + from ui.theme_manager import theme_manager + + current_is_dark = theme_manager.is_dark_theme() + theme_manager.set_dark_theme(not current_is_dark) + + # 更新对话显示 + self.rebuild_conversation_display() - def apply_theme(self, theme): - """应用主题样式""" - self.current_theme = theme - - if theme == "dark": - # 深色主题样式 - self.setStyleSheet(""" - QDialog { - background-color: #1e1e1e; - color: #ffffff; - } - QLabel { - color: #ffffff; - } - QTextEdit { - background-color: #2d2d2d; - color: #ffffff; - border: 1px solid #444; - border-radius: 4px; - padding: 10px; - font-family: 'Microsoft YaHei', sans-serif; - font-size: 12px; - } - QLineEdit { - background-color: #2d2d2d; - color: #ffffff; - border: 1px solid #444; - border-radius: 4px; - padding: 8px; - font-size: 12px; - } - QPushButton { - background-color: #0078d7; - color: white; - border: none; - border-radius: 4px; - padding: 8px 16px; - font-size: 12px; - } - QPushButton:hover { - background-color: #106ebe; - } - QPushButton:pressed { - background-color: #005a9e; - } - QScrollArea { - background-color: #1e1e1e; - border: none; - } - QScrollBar:vertical { - background-color: #2d2d2d; - width: 15px; - margin: 0px; - } - QScrollBar::handle:vertical { - background-color: #555; - border-radius: 7px; - min-height: 20px; - } - QScrollBar::handle:vertical:hover { - background-color: #666; - } - """) - else: - # 浅色主题样式 - self.setStyleSheet(""" - QDialog { - background-color: #ffffff; - color: #000000; - } - QLabel { - color: #000000; - } - QTextEdit { - background-color: #ffffff; - color: #000000; - border: 1px solid #ddd; - border-radius: 4px; - padding: 10px; - font-family: 'Microsoft YaHei', sans-serif; - font-size: 12px; - } - QLineEdit { - background-color: #ffffff; - color: #000000; - border: 1px solid #ddd; - border-radius: 4px; - padding: 8px; - font-size: 12px; - } - QPushButton { - background-color: #0078d7; - color: white; - border: none; - border-radius: 4px; - padding: 8px 16px; - font-size: 12px; - } - QPushButton:hover { - background-color: #106ebe; - } - QPushButton:pressed { - background-color: #005a9e; - } - QScrollArea { - background-color: #ffffff; - border: none; - } - QScrollBar:vertical { - background-color: #f0f0f0; - width: 15px; - margin: 0px; - } - QScrollBar::handle:vertical { - background-color: #c0c0c0; - border-radius: 7px; - min-height: 20px; - } - QScrollBar::handle:vertical:hover { - background-color: #a0a0a0; - } - """) + def apply_theme(self, is_dark=None): + """应用主题样式 - 与主题管理器同步""" + try: + from .ui.theme_manager import theme_manager + except ImportError: + # 处理直接运行的情况 + from ui.theme_manager import theme_manager + + if is_dark is None: + is_dark = theme_manager.is_dark_theme() + + # 获取主题管理器的样式表 + base_stylesheet = theme_manager.get_theme_stylesheet(is_dark) + + # 添加对话框特定的样式优化 + dialog_stylesheet = base_stylesheet + f""" + /* DeepSeek对话框特定样式 */ + QDialog {{ + background-color: {'#1c1c1e' if is_dark else '#ffffff'}; + }} + + /* 对话区域优化 */ + QTextEdit#conversation_text {{ + background-color: {'#121212' if is_dark else '#ffffff'}; + border: 1px solid {'#3a3a3c' if is_dark else '#e0e0e0'}; + border-radius: 8px; + padding: 16px; + font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif; + font-size: 14px; + line-height: 1.6; + color: {'#e8e8ed' if is_dark else '#333333'}; + }} + + /* 输入框优化 */ + QTextEdit#input_edit {{ + background-color: {'#2c2c2e' if is_dark else '#f8f8f8'}; + border: 1px solid {'#3a3a3c' if is_dark else '#e0e0e0'}; + border-radius: 8px; + padding: 12px; + font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif; + font-size: 14px; + color: {'#e8e8ed' if is_dark else '#333333'}; + }} + + QTextEdit#input_edit:focus {{ + border: 2px solid {'#0a84ff' if is_dark else '#0078d7'}; + }} + + /* 按钮优化 */ + QPushButton {{ + border-radius: 8px; + padding: 8px 16px; + font-weight: 500; + min-width: 80px; + }} + + QPushButton:default {{ + background-color: #0a84ff; + color: #ffffff; + }} + + /* 滚动区域优化 */ + QScrollArea {{ + border: none; + background-color: transparent; + }} + + /* 消息气泡样式 */ + .user-message {{ + background-color: {'#0a84ff' if is_dark else '#0078d7'}; + color: #ffffff; + border-radius: 12px; + padding: 12px 16px; + margin: 4px 0; + max-width: 80%; + align-self: flex-end; + }} + + .ai-message {{ + background-color: {'#3a3a3c' if is_dark else '#f0f0f0'}; + color: {'#e8e8ed' if is_dark else '#333333'}; + border-radius: 12px; + padding: 12px 16px; + margin: 4px 0; + max-width: 80%; + align-self: flex-start; + }} + """ + + self.setStyleSheet(dialog_stylesheet) def create_conversation_area(self, parent): """创建对话显示区域""" @@ -413,7 +388,6 @@ class DeepSeekDialogWindow(QDialog): self.add_streaming_message_start() # 在新线程中执行流式请求 - import threading self.streaming_thread = threading.Thread(target=self.call_deepseek_api_stream, args=(message,)) self.streaming_thread.daemon = True self.streaming_thread.start() @@ -455,7 +429,14 @@ class DeepSeekDialogWindow(QDialog): self.conversation_text.ensureCursorVisible() def rebuild_conversation_display(self): - """重新构建对话显示""" + """重新构建对话显示 - 优化主题适配""" + try: + from .ui.theme_manager import theme_manager + except ImportError: + # 处理直接运行的情况 + from ui.theme_manager import theme_manager + + is_dark = theme_manager.is_dark_theme() html_content = "" for msg in self.conversation_history: @@ -463,29 +444,66 @@ class DeepSeekDialogWindow(QDialog): message = msg["message"] is_streaming = msg.get("streaming", False) - # 根据发送者设置不同的样式 + # 根据发送者和主题设置不同的样式 - 优化颜色对比度 if sender == "用户": - bg_color = "#e3f2fd" if self.current_theme == "light" else "#2d3e50" - text_color = "#000" if self.current_theme == "light" else "#fff" + bg_color = "#0a84ff" # 统一的用户消息颜色 + text_color = "#ffffff" + align_style = "margin-left: auto; margin-right: 0;" elif sender == "AI助手": - bg_color = "#f5f5f5" if self.current_theme == "light" else "#3d3d3d" - text_color = "#000" if self.current_theme == "light" else "#fff" + bg_color = "#3a3a3c" if is_dark else "#f0f0f0" + text_color = "#e8e8ed" if is_dark else "#333333" + align_style = "margin-left: 0; margin-right: auto;" else: # 系统消息 - bg_color = "#fff3cd" if self.current_theme == "light" else "#5d4e00" - text_color = "#856404" if self.current_theme == "light" else "#ffd700" + bg_color = "#5d4e00" if is_dark else "#fff3cd" + text_color = "#ffd700" if is_dark else "#856404" + align_style = "margin: 0 auto;" # 格式化消息内容 formatted_message = message.replace('\n', '
') if message else "正在思考..." + # 优化消息气泡样式 html_content += f''' -
- {sender}:
- {formatted_message} +
+
{sender}:
+
{formatted_message}
''' + # 添加现代滚动条样式 + scrollbar_style = f""" + + """ + # 设置HTML内容 - self.conversation_text.setHtml(html_content) + self.conversation_text.setHtml(scrollbar_style + html_content) def call_deepseek_api_stream(self, message): """调用DeepSeek API(流式版本)""" diff --git a/src/ui/calendar_floating_widget.py b/src/ui/calendar_floating_widget.py index f8ee8ca..90f0cde 100644 --- a/src/ui/calendar_floating_widget.py +++ b/src/ui/calendar_floating_widget.py @@ -133,12 +133,12 @@ class CalendarFloatingWidget(QWidget): self.apply_theme() def apply_theme(self): - """应用主题样式""" + """应用主题样式 - 优化Apple设计风格""" is_dark = theme_manager.is_dark_theme() colors = theme_manager.get_current_theme_colors() if is_dark: - # 深色主题样式 + # 深色主题样式 - 优化版Apple设计风格 self.main_frame.setStyleSheet(f""" QFrame#mainFrame {{ background-color: {colors['surface']}; @@ -153,7 +153,7 @@ class CalendarFloatingWidget(QWidget): }} QLabel#dateLabel {{ color: {colors['text_secondary']}; - font-size: 11px; + font-size: 12px; padding: 4px 6px; margin: 2px; }} @@ -174,40 +174,49 @@ class CalendarFloatingWidget(QWidget): color: white; border-radius: 6px; }} + QPushButton#closeButton:pressed {{ + background-color: #c50e1f; + }} QPushButton#todayButton, QPushButton#clearButton, QPushButton#insertButton {{ background-color: {colors['accent']}; color: white; border: none; border-radius: 6px; padding: 6px 16px; - font-size: 11px; + font-size: 12px; font-weight: 500; }} QPushButton#todayButton:hover, QPushButton#clearButton:hover, QPushButton#insertButton:hover {{ background-color: {colors['accent_hover']}; }} + QPushButton#todayButton:pressed, QPushButton#clearButton:pressed, QPushButton#insertButton:pressed {{ + background-color: {colors['accent_pressed']}; + }} """) - # 更新日历控件样式 + # 更新日历控件样式 - 深色主题优化版Apple设计风格 self.calendar.setStyleSheet(f""" QCalendarWidget {{ background-color: {colors['surface']}; border: 1px solid {colors['border']}; - border-radius: 4px; + border-radius: 8px; }} QCalendarWidget QToolButton {{ - height: 30px; - width: 80px; + height: 32px; + width: 85px; color: {colors['text']}; - font-size: 12px; - font-weight: bold; + font-size: 13px; + font-weight: 500; background-color: {colors['surface']}; border: 1px solid {colors['border']}; - border-radius: 4px; + border-radius: 6px; }} QCalendarWidget QToolButton:hover {{ background-color: {colors['surface_hover']}; }} + QCalendarWidget QToolButton:pressed {{ + background-color: {colors['surface_pressed']}; + }} QCalendarWidget QMenu {{ width: 150px; left: 20px; @@ -215,15 +224,16 @@ class CalendarFloatingWidget(QWidget): font-size: 12px; background-color: {colors['surface']}; border: 1px solid {colors['border']}; + border-radius: 6px; }} QCalendarWidget QSpinBox {{ - width: 80px; + width: 85px; font-size: 12px; background-color: {colors['surface']}; selection-background-color: {colors['accent']}; selection-color: white; border: 1px solid {colors['border']}; - border-radius: 4px; + border-radius: 6px; color: {colors['text']}; }} QCalendarWidget QSpinBox::up-button {{ @@ -254,12 +264,15 @@ class CalendarFloatingWidget(QWidget): background-color: {colors['surface']}; color: {colors['text']}; }} + QCalendarWidget QAbstractItemView:disabled {{ + color: {colors['text_disabled']}; + }} QCalendarWidget QWidget#qt_calendar_navigationbar {{ background-color: {colors['surface']}; }} """) else: - # 浅色主题样式 + # 浅色主题样式 - 优化版Apple设计风格 self.main_frame.setStyleSheet(f""" QFrame#mainFrame {{ background-color: {colors['surface']}; @@ -275,7 +288,7 @@ class CalendarFloatingWidget(QWidget): }} QLabel#dateLabel {{ color: {colors['text_secondary']}; - font-size: 11px; + font-size: 12px; padding: 4px 6px; margin: 2px; }} @@ -296,40 +309,49 @@ class CalendarFloatingWidget(QWidget): color: white; border-radius: 6px; }} + QPushButton#closeButton:pressed {{ + background-color: #c50e1f; + }} QPushButton#todayButton, QPushButton#clearButton, QPushButton#insertButton {{ background-color: {colors['accent']}; color: white; border: none; border-radius: 6px; padding: 6px 16px; - font-size: 11px; + font-size: 12px; font-weight: 500; }} QPushButton#todayButton:hover, QPushButton#clearButton:hover, QPushButton#insertButton:hover {{ background-color: {colors['accent_hover']}; }} + QPushButton#todayButton:pressed, QPushButton#clearButton:pressed, QPushButton#insertButton:pressed {{ + background-color: {colors['accent_pressed']}; + }} """) - # 更新日历控件样式 + # 更新日历控件样式 - 浅色主题优化版Apple设计风格 self.calendar.setStyleSheet(f""" QCalendarWidget {{ background-color: {colors['surface']}; border: 1px solid {colors['border']}; - border-radius: 4px; + border-radius: 8px; }} QCalendarWidget QToolButton {{ - height: 30px; - width: 80px; + height: 32px; + width: 85px; color: {colors['text']}; - font-size: 12px; - font-weight: bold; + font-size: 13px; + font-weight: 500; background-color: {colors['surface']}; border: 1px solid {colors['border']}; - border-radius: 4px; + border-radius: 6px; }} QCalendarWidget QToolButton:hover {{ background-color: {colors['surface_hover']}; }} + QCalendarWidget QToolButton:pressed {{ + background-color: {colors['surface_pressed']}; + }} QCalendarWidget QMenu {{ width: 150px; left: 20px; @@ -337,15 +359,16 @@ class CalendarFloatingWidget(QWidget): font-size: 12px; background-color: {colors['surface']}; border: 1px solid {colors['border']}; + border-radius: 6px; }} QCalendarWidget QSpinBox {{ - width: 80px; + width: 85px; font-size: 12px; background-color: {colors['surface']}; selection-background-color: {colors['accent']}; selection-color: white; border: 1px solid {colors['border']}; - border-radius: 4px; + border-radius: 6px; color: {colors['text']}; }} QCalendarWidget QSpinBox::up-button {{ @@ -376,6 +399,9 @@ class CalendarFloatingWidget(QWidget): background-color: {colors['surface']}; color: {colors['text']}; }} + QCalendarWidget QAbstractItemView:disabled {{ + color: {colors['text_disabled']}; + }} QCalendarWidget QWidget#qt_calendar_navigationbar {{ background-color: {colors['surface']}; }} diff --git a/src/ui/calendar_widget.py b/src/ui/calendar_widget.py index 5331faf..f3351b0 100644 --- a/src/ui/calendar_widget.py +++ b/src/ui/calendar_widget.py @@ -277,153 +277,205 @@ class CalendarWidget(QWidget): theme_manager.theme_changed.connect(self.on_theme_changed) # 应用当前主题 - self.apply_theme() + current_theme = theme_manager.is_dark_theme() + self.apply_theme(current_theme) - def apply_theme(self): - """应用主题样式""" + def apply_theme(self, is_dark_theme): + """应用主题样式 - 优化Apple设计风格""" is_dark = theme_manager.is_dark_theme() if is_dark: - # 深色主题样式 + # 深色主题样式 - 优化版Apple设计风格 self.setStyleSheet(""" QWidget { - background-color: #2c2c2e; - color: #f0f0f0; + background-color: #1c1c1e; + color: #e8e8ed; } """) # 更新日历控件样式 self.calendar.setStyleSheet(""" QCalendarWidget { - background-color: #2c2c2e; - border: 1px solid #404040; - border-radius: 4px; + background-color: #1c1c1e; + border: 1px solid #3a3a3c; + border-radius: 8px; } QCalendarWidget QToolButton { - height: 30px; - width: 80px; - color: #f0f0f0; - font-size: 12px; - font-weight: bold; - background-color: #3a3a3c; - border: 1px solid #4a4a4c; - border-radius: 4px; + height: 32px; + width: 85px; + color: #e8e8ed; + font-size: 13px; + font-weight: 500; + background-color: #2c2c2e; + border: 1px solid #3a3a3c; + border-radius: 6px; } - QCalendarWidget QToolButton:hover { + QCalendarWidget QToolButton:pressed { background-color: #4a4a4c; + border: 1px solid #5a5a5c; } - QCalendarWidget QMenu { - width: 150px; - left: 20px; - color: #f0f0f0; - font-size: 12px; + QCalendarWidget QToolButton:hover { background-color: #3a3a3c; border: 1px solid #4a4a4c; } + QCalendarWidget QMenu { + width: 160px; + left: 20px; + color: #e8e8ed; + font-size: 13px; + background-color: #2c2c2e; + border: 1px solid #3a3a3c; + border-radius: 6px; + } + QCalendarWidget QMenu::item:selected { + background-color: #0a84ff; + color: #ffffff; + } QCalendarWidget QSpinBox { - width: 80px; - font-size: 12px; - background-color: #3a3a3c; + width: 85px; + font-size: 13px; + background-color: #2c2c2e; selection-background-color: #0a84ff; selection-color: #ffffff; - border: 1px solid #4a4a4c; - border-radius: 4px; - color: #f0f0f0; + border: 1px solid #3a3a3c; + border-radius: 6px; + color: #e8e8ed; + padding: 2px; } QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; - width: 20px; + width: 22px; + border: 1px solid #3a3a3c; + background-color: #2c2c2e; + border-radius: 0 6px 0 0; } QCalendarWidget QSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; - width: 20px; + width: 22px; + border: 1px solid #3a3a3c; + background-color: #2c2c2e; + border-radius: 0 0 6px 0; + } + QCalendarWidget QSpinBox::up-button:hover, + QCalendarWidget QSpinBox::down-button:hover { + background-color: #3a3a3c; + } + QCalendarWidget QSpinBox::up-button:pressed, + QCalendarWidget QSpinBox::down-button:pressed { + background-color: #4a4a4c; } QCalendarWidget QSpinBox::up-arrow { - width: 10px; - height: 10px; + width: 12px; + height: 12px; } QCalendarWidget QSpinBox::down-arrow { - width: 10px; - height: 10px; + width: 12px; + height: 12px; } QCalendarWidget QWidget { - alternate-background-color: #3a3a3c; + alternate-background-color: #2c2c2e; } QCalendarWidget QAbstractItemView:enabled { - font-size: 12px; + font-size: 13px; selection-background-color: #0a84ff; selection-color: #ffffff; - background-color: #2c2c2e; - color: #f0f0f0; + background-color: #121212; + color: #e8e8ed; + } + QCalendarWidget QAbstractItemView:disabled { + color: #8a8a8d; } QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: #3a3a3c; + background-color: #2c2c2e; } """) # 更新标签样式 - self.date_label.setStyleSheet("QLabel { color: #a0a0a0; }") + self.date_label.setStyleSheet("QLabel { color: #8a8a8d; font-size: 12px; font-weight: 500; }") # 更新按钮样式 self.close_btn.setStyleSheet(""" QPushButton { + background-color: #2c2c2e; + border: 1px solid #3a3a3c; + border-radius: 14px; + font-size: 18px; + font-weight: 600; + color: #e8e8ed; + padding: 6px; + } + QPushButton:hover { background-color: #3a3a3c; border: 1px solid #4a4a4c; - border-radius: 12px; - font-size: 16px; - font-weight: bold; - color: #f0f0f0; } - QPushButton:hover { + QPushButton:pressed { background-color: #4a4a4c; + border: 1px solid #5a5a5c; } """) self.today_btn.setStyleSheet(""" QPushButton { background-color: #0a84ff; - color: white; + color: #ffffff; border: none; - border-radius: 4px; - padding: 5px 10px; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; } QPushButton:hover { - background-color: #0066cc; + background-color: #0071e3; + } + QPushButton:pressed { + background-color: #0051d5; } """) self.clear_btn.setStyleSheet(""" QPushButton { + background-color: #2c2c2e; + color: #e8e8ed; + border: 1px solid #3a3a3c; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; + } + QPushButton:hover { background-color: #3a3a3c; - color: #f0f0f0; border: 1px solid #4a4a4c; - border-radius: 4px; - padding: 5px 10px; } - QPushButton:hover { + QPushButton:pressed { background-color: #4a4a4c; + border: 1px solid #5a5a5c; } """) self.insert_btn.setStyleSheet(""" QPushButton { - background-color: #32d74b; - color: #000000; + background-color: #34c759; + color: #ffffff; border: none; - border-radius: 4px; - padding: 5px 10px; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; } QPushButton:hover { - background-color: #24b334; + background-color: #30d158; + } + QPushButton:pressed { + background-color: #2eb750; } """) else: - # 浅色主题样式 + # 浅色主题样式 - 优化版Apple设计风格 self.setStyleSheet(""" QWidget { - background-color: white; + background-color: #f8f8f8; color: #333333; } """) @@ -431,68 +483,96 @@ class CalendarWidget(QWidget): # 更新日历控件样式 self.calendar.setStyleSheet(""" QCalendarWidget { - background-color: white; - border: 1px solid #ccc; - border-radius: 4px; + background-color: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 6px; } QCalendarWidget QToolButton { - height: 30px; - width: 80px; - color: #333; - font-size: 12px; - font-weight: bold; + height: 32px; + width: 85px; + color: #333333; + font-size: 13px; + font-weight: 500; + background-color: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 6px; + } + QCalendarWidget QToolButton:pressed { background-color: #f0f0f0; - border: 1px solid #ccc; - border-radius: 4px; + border: 1px solid #c0c0c0; } QCalendarWidget QToolButton:hover { - background-color: #e0e0e0; + background-color: #f0f0f0; + border: 1px solid #d0d0d0; } QCalendarWidget QMenu { - width: 150px; + width: 160px; left: 20px; - color: #333; - font-size: 12px; - background-color: white; - border: 1px solid #ccc; + color: #333333; + font-size: 13px; + background-color: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 6px; + } + QCalendarWidget QMenu::item:selected { + background-color: #007aff; + color: #ffffff; } QCalendarWidget QSpinBox { - width: 80px; - font-size: 12px; - background-color: #f0f0f0; - selection-background-color: #0078d7; - selection-color: white; - border: 1px solid #ccc; - border-radius: 4px; - color: #333; + width: 85px; + font-size: 13px; + background-color: #ffffff; + selection-background-color: #007aff; + selection-color: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 6px; + color: #333333; + padding: 2px; } QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; - width: 20px; + width: 22px; + border: 1px solid #e0e0e0; + background-color: #ffffff; + border-radius: 0 6px 0 0; } QCalendarWidget QSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; - width: 20px; + width: 22px; + border: 1px solid #e0e0e0; + background-color: #ffffff; + border-radius: 0 0 6px 0; + } + QCalendarWidget QSpinBox::up-button:hover, + QCalendarWidget QSpinBox::down-button:hover { + background-color: #f0f0f0; + } + QCalendarWidget QSpinBox::up-button:pressed, + QCalendarWidget QSpinBox::down-button:pressed { + background-color: #e0e0e0; } QCalendarWidget QSpinBox::up-arrow { - width: 10px; - height: 10px; + width: 12px; + height: 12px; } QCalendarWidget QSpinBox::down-arrow { - width: 10px; - height: 10px; + width: 12px; + height: 12px; } QCalendarWidget QWidget { - alternate-background-color: #f0f0f0; + alternate-background-color: #f8f8f8; } QCalendarWidget QAbstractItemView:enabled { - font-size: 12px; - selection-background-color: #0078d7; - selection-color: white; - background-color: white; - color: #333; + font-size: 13px; + selection-background-color: #007aff; + selection-color: #ffffff; + background-color: #ffffff; + color: #333333; + } + QCalendarWidget QAbstractItemView:disabled { + color: #999999; } QCalendarWidget QWidget#qt_calendar_navigationbar { background-color: #f8f8f8; @@ -500,65 +580,88 @@ class CalendarWidget(QWidget): """) # 更新标签样式 - self.date_label.setStyleSheet("QLabel { color: #666; }") + self.date_label.setStyleSheet("QLabel { color: #666666; font-size: 12px; font-weight: 500; }") # 更新按钮样式 self.close_btn.setStyleSheet(""" QPushButton { - background-color: #f0f0f0; - border: 1px solid #ccc; - border-radius: 12px; - font-size: 16px; - font-weight: bold; - color: #333; + background-color: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 14px; + font-size: 18px; + font-weight: 600; + color: #333333; + padding: 6px; } QPushButton:hover { + background-color: #f0f0f0; + border: 1px solid #d0d0d0; + } + QPushButton:pressed { background-color: #e0e0e0; + border: 1px solid #c0c0c0; } """) self.today_btn.setStyleSheet(""" QPushButton { - background-color: #0078d7; - color: white; + background-color: #007aff; + color: #ffffff; border: none; - border-radius: 4px; - padding: 5px 10px; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; } QPushButton:hover { - background-color: #005a9e; + background-color: #0056b3; + } + QPushButton:pressed { + background-color: #004494; } """) self.clear_btn.setStyleSheet(""" QPushButton { - background-color: #f0f0f0; - color: #333; - border: 1px solid #ccc; - border-radius: 4px; - padding: 5px 10px; + background-color: #ffffff; + color: #333333; + border: 1px solid #e0e0e0; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; } QPushButton:hover { + background-color: #f0f0f0; + border: 1px solid #d0d0d0; + } + QPushButton:pressed { background-color: #e0e0e0; + border: 1px solid #c0c0c0; } """) self.insert_btn.setStyleSheet(""" QPushButton { - background-color: #4CAF50; - color: white; + background-color: #34c759; + color: #ffffff; border: none; - border-radius: 4px; - padding: 5px 10px; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; } QPushButton:hover { - background-color: #45a049; + background-color: #2e8b57; + } + QPushButton:pressed { + background-color: #267349; } """) def on_theme_changed(self, is_dark): """主题切换槽函数""" - self.apply_theme() + self.apply_theme(is_dark) if __name__ == "__main__": diff --git a/src/ui/theme_manager.py b/src/ui/theme_manager.py index 0978872..52d4578 100644 --- a/src/ui/theme_manager.py +++ b/src/ui/theme_manager.py @@ -153,67 +153,67 @@ class ThemeManager(QObject): return self._get_light_stylesheet() def _get_dark_stylesheet(self): - """深色主题样式表 - Apple设计风格""" + """深色主题样式表 - 优化版Apple设计风格""" return """ - /* Apple设计风格深色主题样式 */ + /* 优化版Apple设计风格深色主题样式 */ /* 全局文字颜色和字体 - 使用Apple系统字体 */ QWidget { - color: #f0f0f0; + color: #e8e8ed; 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; + background-color: #1c1c1e; } - /* 菜单栏 - Apple深色风格 */ + /* 菜单栏 - 优化版Apple深色风格 */ QMenuBar { background-color: #2c2c2e; border: none; - border-bottom: 1px solid #404040; + border-bottom: 1px solid #3a3a3c; font-size: 13px; - color: #f0f0f0; + color: #e8e8ed; padding: 4px 0; } QMenuBar::item { background-color: transparent; padding: 6px 12px; - color: #f0f0f0; - border-radius: 4px; - margin: 0 1px; + color: #e8e8ed; + border-radius: 6px; + margin: 0 2px; } QMenuBar::item:selected { - background-color: #404040; - color: #f0f0f0; + background-color: #3a3a3c; + color: #e8e8ed; } QMenuBar::item:pressed { - background-color: #505050; - color: #f0f0f0; + background-color: #4a4a4c; + color: #e8e8ed; } - /* 菜单 - Apple深色风格 */ + /* 菜单 - 优化版Apple深色风格 */ QMenu { background-color: #2c2c2e; - border: 1px solid #404040; + border: 1px solid #3a3a3c; border-radius: 8px; font-size: 13px; - color: #f0f0f0; + color: #e8e8ed; padding: 4px 0; margin: 2px; } QMenu::item { - color: #f0f0f0; + color: #e8e8ed; background-color: transparent; - border-radius: 4px; + border-radius: 6px; margin: 0 4px; - padding: 4px 20px; + padding: 6px 20px; } QMenu::item:selected { @@ -228,22 +228,22 @@ class ThemeManager(QObject): QMenu::separator { height: 1px; - background-color: #404040; + background-color: #3a3a3c; margin: 4px 8px; } - /* 功能区 */ + /* 功能区 - 优化背景色 */ QFrame { - background-color: #2c2c2e; + background-color: #1c1c1e; border: none; } - /* 组框 */ + /* 组框 - 优化标题颜色 */ QGroupBox { font-size: 12px; font-weight: normal; - color: #f0f0f0; - background-color: #2c2c2e; + color: #e8e8ed; + background-color: #1c1c1e; border: none; border-radius: 8px; margin-top: 5px; @@ -254,27 +254,27 @@ class ThemeManager(QObject): subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; - color: #a0a0a0; + color: #8a8a8d; } - /* 工具按钮 - Apple深色风格 */ + /* 工具按钮 - 优化版Apple深色风格 */ QToolButton { border: 1px solid transparent; border-radius: 6px; - background-color: #3a3a3c; + background-color: #2c2c2e; font-size: 13px; - color: #f0f0f0; + color: #e8e8ed; padding: 6px 12px; } QToolButton:hover { - background-color: #4a4a4c; - border: 1px solid #5a5a5c; + background-color: #3a3a3c; + border: 1px solid #4a4a4c; } QToolButton:pressed { - background-color: #5a5a5c; - border: 1px solid #6a6a6c; + background-color: #4a4a4c; + border: 1px solid #5a5a5c; } QToolButton:checked { @@ -283,18 +283,18 @@ class ThemeManager(QObject): color: #ffffff; } - /* 切换按钮 */ + /* 切换按钮 - 优化样式 */ QToolButton[checkable="true"] { - border: 1px solid #4a4a4c; + border: 1px solid #3a3a3c; border-radius: 6px; - background-color: #3a3a3c; + background-color: #2c2c2e; font-size: 12px; - color: #f0f0f0; + color: #e8e8ed; padding: 6px 12px; } QToolButton[checkable="true"]:hover { - background-color: #4a4a4c; + background-color: #3a3a3c; } QToolButton[checkable="true"]:checked { @@ -303,26 +303,26 @@ class ThemeManager(QObject): color: #ffffff; } - /* 下拉框 - Apple深色风格 */ + /* 下拉框 - 优化版Apple深色风格 */ QComboBox { - background-color: #3a3a3c; - border: 1px solid #4a4a4c; + background-color: #2c2c2e; + border: 1px solid #3a3a3c; border-radius: 6px; - color: #f0f0f0; + color: #e8e8ed; padding: 4px 8px; selection-background-color: #0a84ff; selection-color: #ffffff; } QComboBox:hover { - background-color: #4a4a4c; - border: 1px solid #5a5a5c; + background-color: #3a3a3c; + border: 1px solid #4a4a4c; } QComboBox::drop-down { border: none; width: 20px; - border-left: 1px solid #4a4a4c; + border-left: 1px solid #3a3a3c; border-top-right-radius: 6px; border-bottom-right-radius: 6px; } @@ -331,44 +331,44 @@ class ThemeManager(QObject): image: none; border-left: 4px solid transparent; border-right: 4px solid transparent; - border-top: 6px solid #a0a0a0; + border-top: 6px solid #8a8a8d; margin: 6px; } QComboBox QAbstractItemView { - background-color: #2c2c2e; - border: 1px solid #4a4a4c; - color: #f0f0f0; + background-color: #1c1c1e; + border: 1px solid #3a3a3c; + color: #e8e8ed; selection-background-color: #0a84ff; selection-color: #ffffff; } - /* 文本编辑区域 - Apple深色风格 */ + /* 文本编辑区域 - 优化版Apple深色风格 */ QTextEdit { - background-color: #1c1c1e; + background-color: #121212; 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; + color: #e8e8ed; padding: 32px; - line-height: 1.5; - selection-background-color: #0066cc; + line-height: 1.6; + selection-background-color: #0a84ff; selection-color: #ffffff; } - /* 状态栏 - Apple深色风格 */ + /* 状态栏 - 优化版Apple深色风格 */ QStatusBar { - background-color: #3a3a3c; - border-top: 1px solid #4a4a4c; + background-color: #2c2c2e; + border-top: 1px solid #3a3a3c; font-size: 12px; - color: #a0a0a0; + color: #8a8a8d; 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; + color: #e8e8ed; background-color: transparent; } @@ -436,24 +436,24 @@ class ThemeManager(QObject): background: none; } - /* 按钮 - Apple深色风格 */ + /* 按钮 - 优化版Apple深色风格 */ QPushButton { - background-color: #3a3a3c; - color: #f0f0f0; - border: 1px solid #4a4a4c; + background-color: #2c2c2e; + color: #e8e8ed; + border: 1px solid #3a3a3c; border-radius: 6px; padding: 6px 16px; font-size: 13px; } QPushButton:hover { - background-color: #4a4a4c; - border: 1px solid #5a5a5c; + background-color: #3a3a3c; + border: 1px solid #4a4a4c; } QPushButton:pressed { - background-color: #5a5a5c; - border: 1px solid #6a6a6c; + background-color: #4a4a4c; + border: 1px solid #5a5a5c; } QPushButton:default { @@ -815,22 +815,28 @@ class ThemeManager(QObject): 'background': '#1e1e1e', 'surface': '#2d2d2d', 'surface_hover': '#3c3c3c', + 'surface_pressed': '#4a4a4c', 'text': '#e0e0e0', 'text_secondary': '#b0b0b0', + 'text_disabled': '#8a8a8d', 'border': '#3c3c3c', 'accent': '#0078d4', - 'accent_hover': '#106ebe' + 'accent_hover': '#106ebe', + 'accent_pressed': '#005a9e' } else: return { 'background': '#f3f2f1', 'surface': '#ffffff', 'surface_hover': '#f0f0f0', + 'surface_pressed': '#e0e0e0', 'text': '#333333', 'text_secondary': '#666666', + 'text_disabled': '#999999', 'border': '#d0d0d0', 'accent': '#0078d7', - 'accent_hover': '#005a9e' + 'accent_hover': '#005a9e', + 'accent_pressed': '#004a99' } diff --git a/src/word_main_window.py b/src/word_main_window.py index 28973f1..bdf3bbc 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -359,7 +359,7 @@ class WordStyleMainWindow(QMainWindow): # 更新日历组件样式 if hasattr(self, 'calendar_widget') and self.calendar_widget is not None: # 日历组件有自己的主题管理机制,只需触发其主题更新 - self.calendar_widget.apply_theme() + self.calendar_widget.apply_theme(is_dark) def update_ribbon_styles(self, is_dark): """更新功能区样式""" From 60be0fda50a1b23abb0959e879595a6f04819afa Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 02:21:45 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0AI=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E7=BB=84=E4=BB=B6=E5=B9=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=BC=96=E8=BE=91=E5=8C=BA=E5=9F=9F=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/ai_chat_panel.py | 381 ++++++++++++++++++++++++++++++++++++++++ src/word_main_window.py | 25 ++- 2 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 src/ui/ai_chat_panel.py diff --git a/src/ui/ai_chat_panel.py b/src/ui/ai_chat_panel.py new file mode 100644 index 0000000..cc0eb68 --- /dev/null +++ b/src/ui/ai_chat_panel.py @@ -0,0 +1,381 @@ +# ai_chat_panel.py - AI对话面板组件 +import json +import os +import requests +import threading +from datetime import datetime +from PyQt5.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLineEdit, + QPushButton, QLabel, QScrollArea, QFrame, QMessageBox +) +from PyQt5.QtCore import Qt, pyqtSignal, QThread, QTimer, QSize, pyqtSlot +from PyQt5.QtGui import QFont, QColor, QTextCursor, QIcon, QPixmap +from PyQt5.QtGui import QTextDocument, QTextCharFormat + +class AIChatPanel(QWidget): + """AI对话面板""" + + # 信号定义 - 用于线程安全的UI更新 + update_chat_display = pyqtSignal(str) # 更新聊天显示信号 + + def __init__(self, parent=None): + super().__init__(parent) + self.api_key = "" + self.conversation_history = [] + self.current_streaming_content = "" + self.is_streaming = False + self.streaming_thread = None + self.streaming_timer = None + + # 加载API密钥 + self.load_api_key() + + self.init_ui() + + # 连接信号到槽 + self.update_chat_display.connect(self.on_update_chat_display) + + def load_api_key(self): + """从本地文件加载API密钥""" + config_file = os.path.join( + os.path.dirname(__file__), + "..", "..", "resources", "config", "deepseek_api.json" + ) + + try: + if os.path.exists(config_file): + with open(config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + self.api_key = config.get('api_key', '') + except Exception as e: + print(f"加载API密钥失败: {e}") + + def init_ui(self): + """初始化UI""" + main_layout = QVBoxLayout() + main_layout.setContentsMargins(8, 8, 8, 8) + main_layout.setSpacing(8) + + # 标题栏 + header_layout = QHBoxLayout() + header_label = QLabel("AI 助手") + header_font = QFont() + header_font.setBold(True) + header_font.setPointSize(11) + header_label.setFont(header_font) + header_layout.addWidget(header_label) + header_layout.addStretch() + + # 清空历史按钮 + clear_btn = QPushButton("清空") + clear_btn.setMaximumWidth(50) + clear_btn.setFont(QFont("微软雅黑", 9)) + clear_btn.clicked.connect(self.clear_history) + header_layout.addWidget(clear_btn) + + main_layout.addLayout(header_layout) + + # 分割线 + line = QFrame() + line.setFrameShape(QFrame.HLine) + line.setStyleSheet("color: #d0d0d0;") + main_layout.addWidget(line) + + # 对话显示区域 + self.chat_display = QTextEdit() + self.chat_display.setReadOnly(True) + self.chat_display.setStyleSheet(""" + QTextEdit { + background-color: #ffffff; + border: 1px solid #d0d0d0; + border-radius: 4px; + font-family: 'Segoe UI', '微软雅黑', sans-serif; + font-size: 10pt; + padding: 8px; + color: #333333; + } + """) + self.chat_display.setMinimumHeight(400) + main_layout.addWidget(self.chat_display) + + # 输入区域 + input_layout = QVBoxLayout() + input_layout.setSpacing(4) + + # 输入框 + self.input_field = QLineEdit() + self.input_field.setPlaceholderText("输入您的问题或请求...") + self.input_field.setStyleSheet(""" + QLineEdit { + background-color: #f9f9f9; + border: 1px solid #d0d0d0; + border-radius: 4px; + font-family: '微软雅黑', sans-serif; + font-size: 10pt; + padding: 6px; + color: #333333; + } + QLineEdit:focus { + border: 1px solid #0078d4; + background-color: #ffffff; + } + """) + self.input_field.returnPressed.connect(self.send_user_message) + input_layout.addWidget(self.input_field) + + # 按钮区域 + button_layout = QHBoxLayout() + button_layout.setSpacing(4) + + # 发送按钮 + self.send_btn = QPushButton("发送") + self.send_btn.setFont(QFont("微软雅黑", 9)) + self.send_btn.setStyleSheet(""" + QPushButton { + background-color: #0078d4; + color: white; + border: none; + border-radius: 4px; + padding: 6px 16px; + font-weight: bold; + } + QPushButton:hover { + background-color: #0063b1; + } + QPushButton:pressed { + background-color: #005a9e; + } + """) + self.send_btn.clicked.connect(self.send_user_message) + button_layout.addStretch() + button_layout.addWidget(self.send_btn) + + input_layout.addLayout(button_layout) + main_layout.addLayout(input_layout) + + self.setLayout(main_layout) + + # 设置背景颜色 + self.setStyleSheet(""" + AIChatPanel { + background-color: #f5f5f5; + border-left: 1px solid #d0d0d0; + } + """) + + def send_user_message(self): + """发送用户消息""" + if not self.api_key: + QMessageBox.warning(self, "警告", "请先配置DeepSeek API密钥") + return + + message = self.input_field.text().strip() + if not message: + return + + # 清空输入框 + self.input_field.clear() + + # 禁用发送按钮 + self.send_btn.setEnabled(False) + self.input_field.setEnabled(False) + + # 显示用户消息 + self.add_message_to_display("用户", message) + + # 添加用户消息到对话历史 + self.conversation_history.append({"sender": "用户", "message": message}) + + # 显示AI正在思考 + self.add_message_to_display("AI助手", "正在思考...") + self.conversation_history.append({"sender": "AI助手", "message": ""}) + + # 在新线程中调用API + self.streaming_thread = threading.Thread( + target=self.call_deepseek_api_stream, + args=(message,) + ) + self.streaming_thread.daemon = True + self.streaming_thread.start() + + # 启动定时器更新显示 + self.streaming_timer = QTimer() + self.streaming_timer.timeout.connect(self.update_streaming_display) + self.streaming_timer.start(100) # 每100毫秒更新一次显示 + + def add_message_to_display(self, sender, message): + """添加消息到显示区域""" + cursor = self.chat_display.textCursor() + cursor.movePosition(QTextCursor.End) + self.chat_display.setTextCursor(cursor) + + # 设置格式 + char_format = QTextCharFormat() + char_format.setFont(QFont("微软雅黑", 10)) + + if sender == "用户": + char_format.setForeground(QColor("#0078d4")) + char_format.setFontWeight(70) + prefix = "你: " + else: # AI助手 + char_format.setForeground(QColor("#333333")) + prefix = "AI: " + + # 插入时间戳 + timestamp_format = QTextCharFormat() + timestamp_format.setForeground(QColor("#999999")) + timestamp_format.setFont(QFont("微软雅黑", 8)) + cursor.insertText(f"\n[{datetime.now().strftime('%H:%M:%S')}] ", timestamp_format) + + # 插入前缀 + cursor.insertText(prefix, char_format) + + # 插入消息 + cursor.insertText(message, char_format) + + # 自动滚动到底部 + self.chat_display.verticalScrollBar().setValue( + self.chat_display.verticalScrollBar().maximum() + ) + + def update_streaming_display(self): + """更新流式显示""" + if self.is_streaming and self.current_streaming_content: + # 重新显示所有对话 + self.rebuild_chat_display() + self.chat_display.verticalScrollBar().setValue( + self.chat_display.verticalScrollBar().maximum() + ) + + def rebuild_chat_display(self): + """重新构建聊天显示""" + self.chat_display.clear() + cursor = self.chat_display.textCursor() + + for msg in self.conversation_history: + sender = msg["sender"] + message = msg["message"] + + # 设置格式 + char_format = QTextCharFormat() + char_format.setFont(QFont("微软雅黑", 10)) + + if sender == "用户": + char_format.setForeground(QColor("#0078d4")) + char_format.setFontWeight(70) + prefix = "你: " + else: + char_format.setForeground(QColor("#333333")) + prefix = "AI: " + + # 插入分隔符 + if self.chat_display.toPlainText(): + cursor.insertText("\n") + + # 插入时间戳 + timestamp_format = QTextCharFormat() + timestamp_format.setForeground(QColor("#999999")) + timestamp_format.setFont(QFont("微软雅黑", 8)) + cursor.insertText(f"[{datetime.now().strftime('%H:%M:%S')}] ", timestamp_format) + + # 插入前缀 + cursor.insertText(prefix, char_format) + + # 插入消息 + cursor.insertText(message, char_format) + + def call_deepseek_api_stream(self, message): + """调用DeepSeek API(流式版本)""" + url = "https://api.deepseek.com/v1/chat/completions" + + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}" + } + + messages = [{"role": "user", "content": message}] + + data = { + "model": "deepseek-chat", + "messages": messages, + "stream": True, + "temperature": 0.7, + "max_tokens": 2000 + } + + self.is_streaming = True + self.current_streaming_content = "" + + try: + response = requests.post(url, headers=headers, json=data, stream=True, timeout=30) + + if response.status_code == 200: + for line in response.iter_lines(): + if line: + line = line.decode('utf-8') + if line.startswith('data: '): + data_str = line[6:] + if data_str == '[DONE]': + break + + try: + data_obj = json.loads(data_str) + if 'choices' in data_obj and len(data_obj['choices']) > 0: + delta = data_obj['choices'][0].get('delta', {}) + if 'content' in delta: + content = delta['content'] + self.current_streaming_content += content + except json.JSONDecodeError: + pass + else: + error_msg = f"API调用失败: {response.status_code}" + self.current_streaming_content = error_msg + + except requests.exceptions.Timeout: + self.current_streaming_content = "请求超时,请重试" + except Exception as e: + self.current_streaming_content = f"错误: {str(e)}" + + finally: + self.is_streaming = False + # 停止定时器 + if self.streaming_timer: + self.streaming_timer.stop() + + # 最后更新一次显示,使用信号在主线程中进行 + self.update_chat_display.emit(self.current_streaming_content) + + @pyqtSlot(str) + def on_update_chat_display(self, content): + """在主线程中更新聊天显示""" + # 更新最后一条AI消息 + if len(self.conversation_history) > 0: + self.conversation_history[-1]["message"] = content + + # 重新构建显示 + self.rebuild_chat_display() + + # 自动滚动到底部 + self.chat_display.verticalScrollBar().setValue( + self.chat_display.verticalScrollBar().maximum() + ) + + # 重新启用输入 + self.send_btn.setEnabled(True) + self.input_field.setEnabled(True) + self.input_field.setFocus() + + def clear_history(self): + """清空聊天历史""" + reply = QMessageBox.question( + self, + "确认", + "确定要清空聊天历史吗?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + + if reply == QMessageBox.Yes: + self.conversation_history = [] + self.chat_display.clear() + self.input_field.clear() diff --git a/src/word_main_window.py b/src/word_main_window.py index bdf3bbc..819f300 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -22,6 +22,7 @@ from ui.calendar_widget import CalendarWidget from ui.weather_floating_widget import WeatherFloatingWidget from ui.quote_floating_widget import QuoteFloatingWidget from ui.calendar_floating_widget import CalendarFloatingWidget +from ui.ai_chat_panel import AIChatPanel # 导入主题管理器 from ui.theme_manager import theme_manager @@ -828,8 +829,12 @@ class WordStyleMainWindow(QMainWindow): def create_document_area(self, main_layout): - """创建文档编辑区域""" + """创建文档编辑区域和AI对话面板""" + # 创建水平分割器 + splitter = QSplitter(Qt.Horizontal) + + # ========== 左侧:文档编辑区域 ========== # 创建滚动区域 from PyQt5.QtWidgets import QScrollArea @@ -906,7 +911,22 @@ class WordStyleMainWindow(QMainWindow): document_container.setLayout(document_layout) self.scroll_area.setWidget(document_container) - main_layout.addWidget(self.scroll_area) + + # ========== 右侧:AI对话面板 ========== + self.ai_chat_panel = AIChatPanel() + self.ai_chat_panel.setMinimumWidth(320) + + # 添加左右两部分到分割器 + splitter.addWidget(self.scroll_area) + splitter.addWidget(self.ai_chat_panel) + + # 设置分割器大小比例(文档区:对话区 = 70:30) + splitter.setSizes([700, 300]) + splitter.setStretchFactor(0, 2) # 文档区可伸缩 + splitter.setStretchFactor(1, 1) # 对话区可伸缩 + + # 添加分割器到主布局 + main_layout.addWidget(splitter) def init_network_services(self): """初始化网络服务""" @@ -920,6 +940,7 @@ class WordStyleMainWindow(QMainWindow): self.quote_thread.quote_fetched.connect(self.update_quote_display) self.quote_thread.start() + def init_typing_logic(self): """初始化打字逻辑""" # 使用默认内容初始化打字逻辑 From 271a2741ff2dc5a3c1d6f31eee1d9567d0b389ca Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 02:31:40 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=B0=8F=E4=BF=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/deepseek_dialog_window.py | 238 ++++++++++++++++++++++++++-------- src/ui/ai_chat_panel.py | 228 ++++++++++++++++++++++++++------ 2 files changed, 374 insertions(+), 92 deletions(-) diff --git a/src/deepseek_dialog_window.py b/src/deepseek_dialog_window.py index 6b6e4d0..80f4ac5 100644 --- a/src/deepseek_dialog_window.py +++ b/src/deepseek_dialog_window.py @@ -137,15 +137,31 @@ class DeepSeekDialogWindow(QDialog): # 主布局 main_layout = QVBoxLayout() + # 标题栏容器 - 包含标题和主题切换按钮 + title_container = QFrame() + title_container_layout = QHBoxLayout() + title_container_layout.setContentsMargins(0, 0, 10, 0) + # 标题 title_label = QLabel("DeepSeek AI对话助手") title_font = QFont() title_font.setPointSize(16) title_font.setBold(True) title_label.setFont(title_font) - title_label.setAlignment(Qt.AlignCenter) - title_label.setStyleSheet("padding: 10px; background-color: #f0f0f0;") - main_layout.addWidget(title_label) + title_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + title_container_layout.addWidget(title_label) + + title_container_layout.addStretch() + + # 主题切换按钮 + theme_button = QPushButton("🌓 切换主题") + theme_button.clicked.connect(self.toggle_theme) + theme_button.setFixedSize(120, 32) + theme_button.setToolTip("点击切换深色/浅色主题") + title_container_layout.addWidget(theme_button) + + title_container.setLayout(title_container_layout) + main_layout.addWidget(title_container) # 分割器 splitter = QSplitter(Qt.Vertical) @@ -162,13 +178,14 @@ class DeepSeekDialogWindow(QDialog): # 状态栏 status_layout = QHBoxLayout() self.status_label = QLabel("就绪") - self.status_label.setStyleSheet("color: #666; padding: 5px;") + self.status_label.setObjectName("status_label") status_layout.addWidget(self.status_label) status_layout.addStretch() # API密钥管理按钮 - api_key_button = QPushButton("管理API密钥") + api_key_button = QPushButton("🔑 管理API密钥") api_key_button.clicked.connect(self.manage_api_key) + api_key_button.setFixedSize(120, 32) status_layout.addWidget(api_key_button) main_layout.addLayout(status_layout) @@ -176,12 +193,7 @@ class DeepSeekDialogWindow(QDialog): self.setLayout(main_layout) # 设置样式 - self.apply_theme("light") # 默认使用浅色主题 - - # 添加主题切换按钮 - theme_button = QPushButton("切换主题") - theme_button.clicked.connect(self.toggle_theme) - status_layout.addWidget(theme_button) + self.apply_theme() # 使用主题管理器自动检测主题 # 连接信号 self.streaming_finished.connect(self.on_streaming_finished) @@ -194,9 +206,13 @@ class DeepSeekDialogWindow(QDialog): # 处理直接运行的情况 from ui.theme_manager import theme_manager + # 切换主题 current_is_dark = theme_manager.is_dark_theme() theme_manager.set_dark_theme(not current_is_dark) + # 重新应用主题 + self.apply_theme() + # 更新对话显示 self.rebuild_conversation_display() @@ -221,12 +237,22 @@ class DeepSeekDialogWindow(QDialog): background-color: {'#1c1c1e' if is_dark else '#ffffff'}; }} + /* 标题栏优化 */ + QLabel {{ + padding: 15px; + background-color: {'#2c2c2e' if is_dark else '#f8f8f8'}; + border-bottom: 1px solid {'#3a3a3c' if is_dark else '#e0e0e0'}; + font-size: 18px; + font-weight: 600; + color: {'#f0f0f0' if is_dark else '#333333'}; + }} + /* 对话区域优化 */ QTextEdit#conversation_text {{ background-color: {'#121212' if is_dark else '#ffffff'}; border: 1px solid {'#3a3a3c' if is_dark else '#e0e0e0'}; - border-radius: 8px; - padding: 16px; + border-radius: 12px; + padding: 20px; font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif; font-size: 14px; line-height: 1.6; @@ -237,8 +263,8 @@ class DeepSeekDialogWindow(QDialog): QTextEdit#input_edit {{ background-color: {'#2c2c2e' if is_dark else '#f8f8f8'}; border: 1px solid {'#3a3a3c' if is_dark else '#e0e0e0'}; - border-radius: 8px; - padding: 12px; + border-radius: 12px; + padding: 15px; font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif; font-size: 14px; color: {'#e8e8ed' if is_dark else '#333333'}; @@ -251,14 +277,37 @@ class DeepSeekDialogWindow(QDialog): /* 按钮优化 */ QPushButton {{ border-radius: 8px; - padding: 8px 16px; + padding: 10px 20px; font-weight: 500; + font-size: 13px; min-width: 80px; + background-color: {'#3a3a3c' if is_dark else '#f0f0f0'}; + color: {'#f0f0f0' if is_dark else '#333333'}; + border: 1px solid {'#4a4a4c' if is_dark else '#d0d0d0'}; + }} + + QPushButton:hover {{ + background-color: {'#4a4a4c' if is_dark else '#e0e0e0'}; + }} + + QPushButton:pressed {{ + background-color: {'#5a5a5c' if is_dark else '#d0d0d0'}; }} 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; }} /* 滚动区域优化 */ @@ -267,25 +316,11 @@ class DeepSeekDialogWindow(QDialog): background-color: transparent; }} - /* 消息气泡样式 */ - .user-message {{ - background-color: {'#0a84ff' if is_dark else '#0078d7'}; - color: #ffffff; - border-radius: 12px; - padding: 12px 16px; - margin: 4px 0; - max-width: 80%; - align-self: flex-end; - }} - - .ai-message {{ - background-color: {'#3a3a3c' if is_dark else '#f0f0f0'}; - color: {'#e8e8ed' if is_dark else '#333333'}; - border-radius: 12px; - padding: 12px 16px; - margin: 4px 0; - max-width: 80%; - align-self: flex-start; + /* 状态栏优化 */ + QLabel#status_label {{ + color: {'#a0a0a0' if is_dark else '#666666'}; + font-size: 12px; + padding: 8px; }} """ @@ -410,10 +445,52 @@ class DeepSeekDialogWindow(QDialog): cursor.movePosition(QTextCursor.End) self.conversation_text.setTextCursor(cursor) - # 添加AI助手的消息框架 + # 添加AI助手的消息框架 - 优化样式 + try: + from .ui.theme_manager import theme_manager + except ImportError: + from ui.theme_manager import theme_manager + + is_dark = theme_manager.is_dark_theme() + + # 使用与普通消息一致的样式 + bg_color = "#3a3a3c" if is_dark else "#f0f0f0" + text_color = "#e8e8ed" if is_dark else "#333333" + box_shadow = "0 2px 8px rgba(0, 0, 0, 0.1);" if not is_dark else "0 2px 8px rgba(0, 0, 0, 0.3);" + self.conversation_text.insertHtml( - f'
' - f'AI助手:
正在思考...' + f'
' + f'
' + f'' + f'AI助手' + f'
' + f'
正在思考...
' + f'
' ) # 自动滚动到底部 @@ -439,66 +516,117 @@ class DeepSeekDialogWindow(QDialog): is_dark = theme_manager.is_dark_theme() html_content = "" - for msg in self.conversation_history: + for i, msg in enumerate(self.conversation_history): sender = msg["sender"] message = msg["message"] is_streaming = msg.get("streaming", False) - # 根据发送者和主题设置不同的样式 - 优化颜色对比度 + # 根据发送者和主题设置不同的样式 - 优化颜色对比度和阴影效果 if sender == "用户": bg_color = "#0a84ff" # 统一的用户消息颜色 text_color = "#ffffff" align_style = "margin-left: auto; margin-right: 0;" + box_shadow = "0 2px 8px rgba(10, 132, 255, 0.3);" if not is_dark else "0 2px 8px rgba(10, 132, 255, 0.5);" elif sender == "AI助手": bg_color = "#3a3a3c" if is_dark else "#f0f0f0" text_color = "#e8e8ed" if is_dark else "#333333" align_style = "margin-left: 0; margin-right: auto;" + box_shadow = "0 2px 8px rgba(0, 0, 0, 0.1);" if not is_dark else "0 2px 8px rgba(0, 0, 0, 0.3);" else: # 系统消息 bg_color = "#5d4e00" if is_dark else "#fff3cd" text_color = "#ffd700" if is_dark else "#856404" align_style = "margin: 0 auto;" + box_shadow = "0 2px 8px rgba(93, 78, 0, 0.2);" if not is_dark else "0 2px 8px rgba(93, 78, 0, 0.4);" - # 格式化消息内容 - formatted_message = message.replace('\n', '
') if message else "正在思考..." + # 格式化消息内容,处理换行和特殊字符 + if message: + # 将换行符转换为
标签 + formatted_message = message.replace('\n', '
') + # 处理多个连续空格 + formatted_message = formatted_message.replace(' ', '  ') + else: + formatted_message = "正在思考..." if is_streaming else "" + + # 为每个消息添加唯一的ID便于调试 + message_id = f"msg-{i}" - # 优化消息气泡样式 + # 优化消息气泡样式 - 添加阴影、更好的边距和动画效果 html_content += f''' -
-
{sender}:
-
{formatted_message}
+
+ + {sender} +
+
{formatted_message}
''' - # 添加现代滚动条样式 + # 添加现代滚动条样式和整体容器样式 scrollbar_style = f""" """ diff --git a/src/ui/ai_chat_panel.py b/src/ui/ai_chat_panel.py index cc0eb68..7db7f25 100644 --- a/src/ui/ai_chat_panel.py +++ b/src/ui/ai_chat_panel.py @@ -12,6 +12,13 @@ from PyQt5.QtCore import Qt, pyqtSignal, QThread, QTimer, QSize, pyqtSlot from PyQt5.QtGui import QFont, QColor, QTextCursor, QIcon, QPixmap from PyQt5.QtGui import QTextDocument, QTextCharFormat +# 导入主题管理器 +try: + from .theme_manager import theme_manager +except ImportError: + # 处理直接运行的情况 + from ui.theme_manager import theme_manager + class AIChatPanel(QWidget): """AI对话面板""" @@ -34,6 +41,9 @@ class AIChatPanel(QWidget): # 连接信号到槽 self.update_chat_display.connect(self.on_update_chat_display) + + # 连接主题变化信号 + theme_manager.theme_changed.connect(self.on_theme_changed) def load_api_key(self): """从本地文件加载API密钥""" @@ -68,9 +78,45 @@ class AIChatPanel(QWidget): # 清空历史按钮 clear_btn = QPushButton("清空") - clear_btn.setMaximumWidth(50) + clear_btn.setObjectName("clear_btn") # 设置对象名称 + clear_btn.setMaximumWidth(60) clear_btn.setFont(QFont("微软雅黑", 9)) clear_btn.clicked.connect(self.clear_history) + clear_btn.setStyleSheet(""" + QPushButton { + background-color: #f0f0f0; + color: #333333; + border: 1px solid #d0d0d0; + border-radius: 6px; + padding: 6px 12px; + font-weight: 500; + font-size: 12px; + min-width: 50px; + } + QPushButton:hover { + background-color: #e0e0e0; + border: 1px solid #c0c0c0; + } + QPushButton:pressed { + background-color: #d0d0d0; + border: 1px solid #b0b0b0; + } + + /* 深色主题 */ + QPushButton[darkTheme="true"] { + background-color: #3c3c3c; + color: #e0e0e0; + border: 1px solid #4c4c4c; + } + QPushButton[darkTheme="true"]:hover { + background-color: #4c4c4c; + border: 1px solid #5c5c5c; + } + QPushButton[darkTheme="true"]:pressed { + background-color: #5c5c5c; + border: 1px solid #6c6c6c; + } + """) header_layout.addWidget(clear_btn) main_layout.addLayout(header_layout) @@ -88,11 +134,19 @@ class AIChatPanel(QWidget): QTextEdit { background-color: #ffffff; border: 1px solid #d0d0d0; - border-radius: 4px; - font-family: 'Segoe UI', '微软雅黑', sans-serif; - font-size: 10pt; - padding: 8px; + border-radius: 8px; + font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif; + font-size: 11pt; + padding: 12px; color: #333333; + line-height: 1.5; + } + + /* 深色主题 */ + QTextEdit[darkTheme="true"] { + background-color: #1e1e1e; + border: 1px solid #3c3c3c; + color: #e0e0e0; } """) self.chat_display.setMinimumHeight(400) @@ -100,7 +154,7 @@ class AIChatPanel(QWidget): # 输入区域 input_layout = QVBoxLayout() - input_layout.setSpacing(4) + input_layout.setSpacing(6) # 输入框 self.input_field = QLineEdit() @@ -109,23 +163,34 @@ class AIChatPanel(QWidget): QLineEdit { background-color: #f9f9f9; border: 1px solid #d0d0d0; - border-radius: 4px; - font-family: '微软雅黑', sans-serif; - font-size: 10pt; - padding: 6px; + border-radius: 8px; + font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif; + font-size: 11pt; + padding: 10px 12px; color: #333333; } QLineEdit:focus { - border: 1px solid #0078d4; + border: 2px solid #0078d4; background-color: #ffffff; } + + /* 深色主题 */ + QLineEdit[darkTheme="true"] { + background-color: #2d2d2d; + border: 1px solid #3c3c3c; + color: #e0e0e0; + } + QLineEdit[darkTheme="true"]:focus { + border: 2px solid #0078d4; + background-color: #1e1e1e; + } """) self.input_field.returnPressed.connect(self.send_user_message) input_layout.addWidget(self.input_field) # 按钮区域 button_layout = QHBoxLayout() - button_layout.setSpacing(4) + button_layout.setSpacing(6) # 发送按钮 self.send_btn = QPushButton("发送") @@ -135,9 +200,11 @@ class AIChatPanel(QWidget): background-color: #0078d4; color: white; border: none; - border-radius: 4px; - padding: 6px 16px; - font-weight: bold; + border-radius: 6px; + padding: 8px 20px; + font-weight: 500; + font-size: 12px; + min-width: 70px; } QPushButton:hover { background-color: #0063b1; @@ -145,6 +212,17 @@ class AIChatPanel(QWidget): QPushButton:pressed { background-color: #005a9e; } + + /* 深色主题 */ + QPushButton[darkTheme="true"] { + background-color: #0078d4; + } + QPushButton[darkTheme="true"]:hover { + background-color: #0063b1; + } + QPushButton[darkTheme="true"]:pressed { + background-color: #005a9e; + } """) self.send_btn.clicked.connect(self.send_user_message) button_layout.addStretch() @@ -161,7 +239,59 @@ class AIChatPanel(QWidget): background-color: #f5f5f5; border-left: 1px solid #d0d0d0; } + + /* 深色主题 */ + AIChatPanel[darkTheme="true"] { + background-color: #2d2d2d; + border-left: 1px solid #3c3c3c; + } """) + + # 初始化主题 + self.apply_theme() + + def apply_theme(self): + """应用当前主题""" + is_dark = theme_manager.is_dark_theme() + + # 设置属性用于样式表选择器 + self.setProperty("darkTheme", is_dark) + + # 更新子控件的属性 + self.chat_display.setProperty("darkTheme", is_dark) + self.input_field.setProperty("darkTheme", is_dark) + self.send_btn.setProperty("darkTheme", is_dark) + self.findChild(QPushButton, "clear_btn").setProperty("darkTheme", is_dark) if self.findChild(QPushButton, "clear_btn") else None + + # 重新应用样式表 + self.style().unpolish(self) + self.style().polish(self) + self.update() + + # 更新聊天显示样式 + self.chat_display.style().unpolish(self.chat_display) + self.chat_display.style().polish(self.chat_display) + self.chat_display.update() + + # 更新输入框样式 + self.input_field.style().unpolish(self.input_field) + self.input_field.style().polish(self.input_field) + self.input_field.update() + + # 更新按钮样式 + self.send_btn.style().unpolish(self.send_btn) + self.send_btn.style().polish(self.send_btn) + self.send_btn.update() + + clear_btn = self.findChild(QPushButton, "clear_btn") + if clear_btn: + clear_btn.style().unpolish(clear_btn) + clear_btn.style().polish(clear_btn) + clear_btn.update() + + def on_theme_changed(self, is_dark): + """主题变化处理""" + self.apply_theme() def send_user_message(self): """发送用户消息""" @@ -211,20 +341,32 @@ class AIChatPanel(QWidget): # 设置格式 char_format = QTextCharFormat() - char_format.setFont(QFont("微软雅黑", 10)) + char_format.setFont(QFont("微软雅黑", 11)) if sender == "用户": - char_format.setForeground(QColor("#0078d4")) - char_format.setFontWeight(70) + # 根据主题设置用户消息颜色 + if theme_manager.is_dark_theme(): + char_format.setForeground(QColor("#4A90E2")) # 深色主题下的蓝色 + else: + char_format.setForeground(QColor("#0078d4")) # 浅色主题下的蓝色 + char_format.setFontWeight(60) # 中等粗体 prefix = "你: " else: # AI助手 - char_format.setForeground(QColor("#333333")) + # 根据主题设置AI消息颜色 + if theme_manager.is_dark_theme(): + char_format.setForeground(QColor("#e0e0e0")) # 深色主题下的浅灰色 + else: + char_format.setForeground(QColor("#000000")) # 浅色主题下的纯黑色,提高可读性 + char_format.setFontWeight(50) # 正常粗体 prefix = "AI: " # 插入时间戳 timestamp_format = QTextCharFormat() - timestamp_format.setForeground(QColor("#999999")) - timestamp_format.setFont(QFont("微软雅黑", 8)) + if theme_manager.is_dark_theme(): + timestamp_format.setForeground(QColor("#a0a0a0")) # 深色主题下的灰色 + else: + timestamp_format.setForeground(QColor("#666666")) # 浅色主题下的深灰色,提高可读性 + timestamp_format.setFont(QFont("微软雅黑", 9)) cursor.insertText(f"\n[{datetime.now().strftime('%H:%M:%S')}] ", timestamp_format) # 插入前缀 @@ -237,16 +379,7 @@ class AIChatPanel(QWidget): self.chat_display.verticalScrollBar().setValue( self.chat_display.verticalScrollBar().maximum() ) - - def update_streaming_display(self): - """更新流式显示""" - if self.is_streaming and self.current_streaming_content: - # 重新显示所有对话 - self.rebuild_chat_display() - self.chat_display.verticalScrollBar().setValue( - self.chat_display.verticalScrollBar().maximum() - ) - + def rebuild_chat_display(self): """重新构建聊天显示""" self.chat_display.clear() @@ -258,14 +391,23 @@ class AIChatPanel(QWidget): # 设置格式 char_format = QTextCharFormat() - char_format.setFont(QFont("微软雅黑", 10)) + char_format.setFont(QFont("微软雅黑", 11)) if sender == "用户": - char_format.setForeground(QColor("#0078d4")) - char_format.setFontWeight(70) + # 根据主题设置用户消息颜色 + if theme_manager.is_dark_theme(): + char_format.setForeground(QColor("#4A90E2")) # 深色主题下的蓝色 + else: + char_format.setForeground(QColor("#0078d4")) # 浅色主题下的蓝色 + char_format.setFontWeight(60) # 中等粗体 prefix = "你: " else: - char_format.setForeground(QColor("#333333")) + # 根据主题设置AI消息颜色 + if theme_manager.is_dark_theme(): + char_format.setForeground(QColor("#e0e0e0")) # 深色主题下的浅灰色 + else: + char_format.setForeground(QColor("#000000")) # 浅色主题下的纯黑色,提高可读性 + char_format.setFontWeight(50) # 正常粗体 prefix = "AI: " # 插入分隔符 @@ -274,8 +416,11 @@ class AIChatPanel(QWidget): # 插入时间戳 timestamp_format = QTextCharFormat() - timestamp_format.setForeground(QColor("#999999")) - timestamp_format.setFont(QFont("微软雅黑", 8)) + if theme_manager.is_dark_theme(): + timestamp_format.setForeground(QColor("#a0a0a0")) # 深色主题下的灰色 + else: + timestamp_format.setForeground(QColor("#666666")) # 浅色主题下的深灰色,提高可读性 + timestamp_format.setFont(QFont("微软雅黑", 9)) cursor.insertText(f"[{datetime.now().strftime('%H:%M:%S')}] ", timestamp_format) # 插入前缀 @@ -379,3 +524,12 @@ class AIChatPanel(QWidget): self.conversation_history = [] self.chat_display.clear() self.input_field.clear() + + def update_streaming_display(self): + """更新流式显示""" + if self.is_streaming and self.current_streaming_content: + # 重新显示所有对话 + self.rebuild_chat_display() + self.chat_display.verticalScrollBar().setValue( + self.chat_display.verticalScrollBar().maximum() + ) \ No newline at end of file From e1f77f7200e0d8a8d4b4f49f4b10f344da2d8a1b Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 13:24:08 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E9=A1=B6=E9=83=A8UI=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/word_main_window.py | 98 ++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 55 deletions(-) diff --git a/src/word_main_window.py b/src/word_main_window.py index 819f300..89b018b 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -573,82 +573,82 @@ class WordStyleMainWindow(QMainWindow): self.menubar = menubar # 保存为实例变量以便后续样式更新 # 文件菜单 - file_menu = menubar.addMenu('文件(F)') + file_menu = menubar.addMenu('文件操作') self.file_menu = file_menu # 保存为实例变量 # 新建 - new_action = QAction('新建(N)', self) + new_action = QAction('新建文档', self) new_action.setShortcut('Ctrl+N') new_action.triggered.connect(self.new_document) file_menu.addAction(new_action) # 导入文件 - 改为导入功能 - open_action = QAction('导入文件(I)...', self) + open_action = QAction('导入文本文件...', self) open_action.setShortcut('Ctrl+O') open_action.triggered.connect(self.import_file) file_menu.addAction(open_action) # 保存 - save_action = QAction('保存(S)', self) + save_action = QAction('保存文档', self) save_action.setShortcut('Ctrl+S') save_action.triggered.connect(self.save_file) file_menu.addAction(save_action) # 另存为 - save_as_action = QAction('另存为(A)...', self) + save_as_action = QAction('另存为...', self) save_as_action.triggered.connect(self.save_as_file) file_menu.addAction(save_as_action) file_menu.addSeparator() # 退出 - exit_action = QAction('退出(X)', self) + exit_action = QAction('退出程序', self) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) - # 开始菜单 - start_menu = menubar.addMenu('开始(S)') - start_menu.setObjectName("startMenu") - self.start_menu = start_menu # 保存为实例变量 + # 编辑菜单 + edit_menu = menubar.addMenu('编辑操作') + edit_menu.setObjectName("editMenu") + self.start_menu = edit_menu # 保存为实例变量 # 撤销 - undo_action = QAction('撤销(U)', self) + undo_action = QAction('撤销操作', self) undo_action.setShortcut('Ctrl+Z') undo_action.triggered.connect(self.undo) - start_menu.addAction(undo_action) + edit_menu.addAction(undo_action) # 重做 - redo_action = QAction('重做(R)', self) + redo_action = QAction('重做操作', self) redo_action.setShortcut('Ctrl+Y') redo_action.triggered.connect(self.redo) - start_menu.addAction(redo_action) + edit_menu.addAction(redo_action) - start_menu.addSeparator() + edit_menu.addSeparator() # 剪切 - cut_action = QAction('剪切(T)', self) + cut_action = QAction('剪切内容', self) cut_action.setShortcut('Ctrl+X') cut_action.triggered.connect(self.cut) - start_menu.addAction(cut_action) + edit_menu.addAction(cut_action) # 复制 - copy_action = QAction('复制(C)', self) + copy_action = QAction('复制内容', self) copy_action.setShortcut('Ctrl+C') copy_action.triggered.connect(self.copy) - start_menu.addAction(copy_action) + edit_menu.addAction(copy_action) # 粘贴 - paste_action = QAction('粘贴(P)', self) + paste_action = QAction('粘贴内容', self) paste_action.setShortcut('Ctrl+V') paste_action.triggered.connect(self.paste) - start_menu.addAction(paste_action) + edit_menu.addAction(paste_action) # 视图菜单 - view_menu = menubar.addMenu('视图(V)') + view_menu = menubar.addMenu('视图设置') self.view_menu = view_menu # 保存为实例变量 # 阅读视图 - read_view_action = QAction('阅读视图', self) + read_view_action = QAction('切换到阅读视图', self) read_view_action.triggered.connect(self.toggle_reading_view) view_menu.addAction(read_view_action) @@ -661,18 +661,18 @@ class WordStyleMainWindow(QMainWindow): view_menu.addSeparator() - # 模式选择子菜单 - theme_menu = view_menu.addMenu('模式') + # 主题模式选择 + theme_menu = view_menu.addMenu('主题模式') # 白色模式 - self.light_mode_action = QAction('白色模式', self) + 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 = 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) @@ -680,56 +680,56 @@ class WordStyleMainWindow(QMainWindow): view_menu.addSeparator() - # 视图模式选择 - view_mode_menu = view_menu.addMenu('视图模式') + # 工作模式选择 + work_mode_menu = view_menu.addMenu('工作模式') # 打字模式 - self.typing_mode_action = QAction('打字模式', self) + self.typing_mode_action = QAction('打字练习模式', self) self.typing_mode_action.setCheckable(True) self.typing_mode_action.setChecked(True) # 默认打字模式 self.typing_mode_action.triggered.connect(lambda: self.set_view_mode("typing")) - view_mode_menu.addAction(self.typing_mode_action) + work_mode_menu.addAction(self.typing_mode_action) # 学习模式 - self.learning_mode_action = QAction('学习模式', self) + self.learning_mode_action = QAction('学习记忆模式', self) self.learning_mode_action.setCheckable(True) self.learning_mode_action.setChecked(False) # 设置学习模式快捷键 (Qt会自动在macOS上映射Ctrl为Cmd) self.learning_mode_action.setShortcut('Ctrl+L') self.learning_mode_action.triggered.connect(lambda: self.set_view_mode("learning")) - view_mode_menu.addAction(self.learning_mode_action) + work_mode_menu.addAction(self.learning_mode_action) view_menu.addSeparator() # 附加工具功能 - weather_menu = view_menu.addMenu('附加工具') + tools_menu = view_menu.addMenu('实用工具') # 刷新天气 refresh_weather_action = QAction('刷新天气', self) refresh_weather_action.setShortcut('F5') refresh_weather_action.triggered.connect(self.refresh_weather) - weather_menu.addAction(refresh_weather_action) + tools_menu.addAction(refresh_weather_action) # 显示详细天气 show_weather_action = QAction('显示详细天气', self) show_weather_action.triggered.connect(self.show_detailed_weather) - weather_menu.addAction(show_weather_action) + tools_menu.addAction(show_weather_action) # 天气悬浮窗口 toggle_floating_weather_action = QAction('天气悬浮窗口', self) toggle_floating_weather_action.triggered.connect(self.toggle_floating_weather) - weather_menu.addAction(toggle_floating_weather_action) + tools_menu.addAction(toggle_floating_weather_action) # 每日谏言悬浮窗口切换动作 toggle_floating_quote_action = QAction('每日谏言悬浮窗口', self) toggle_floating_quote_action.triggered.connect(self.toggle_floating_quote) - weather_menu.addAction(toggle_floating_quote_action) + tools_menu.addAction(toggle_floating_quote_action) # 日历悬浮窗口切换动作 toggle_floating_calendar_action = QAction('日历悬浮窗口', self) toggle_floating_calendar_action.triggered.connect(self.toggle_floating_calendar) - weather_menu.addAction(toggle_floating_calendar_action) + tools_menu.addAction(toggle_floating_calendar_action) # 插入菜单 insert_menu = menubar.addMenu('插入(I)') @@ -787,29 +787,17 @@ class WordStyleMainWindow(QMainWindow): export_docx_action.triggered.connect(self.export_as_docx) export_menu.addAction(export_docx_action) - # 布局菜单 - layout_menu = menubar.addMenu('布局(L)') - # 引用菜单 - reference_menu = menubar.addMenu('引用(R)') + reference_menu = menubar.addMenu('AI功能') # DeepSeek AI对话功能 - self.deepseek_dialog_action = QAction('DeepSeek AI对话', self) + self.deepseek_dialog_action = QAction('AI智能对话', self) self.deepseek_dialog_action.setShortcut('Ctrl+D') self.deepseek_dialog_action.triggered.connect(self.open_deepseek_dialog) reference_menu.addAction(self.deepseek_dialog_action) - # 邮件菜单 - mail_menu = menubar.addMenu('邮件(M)') - - # 审阅菜单 - review_menu = menubar.addMenu('审阅(W)') - - # 开发工具菜单 - developer_menu = menubar.addMenu('开发工具(Q)') - # 应用选项菜单 - app_menu = menubar.addMenu('应用选项(O)') + app_menu = menubar.addMenu('应用选项') # 小游戏子菜单 games_menu = app_menu.addMenu('小游戏') @@ -820,7 +808,7 @@ class WordStyleMainWindow(QMainWindow): games_menu.addAction(snake_game_action) # 帮助菜单 - help_menu = menubar.addMenu('帮助(H)') + help_menu = menubar.addMenu('帮助') # 关于 about_action = QAction('关于 MagicWord', self) From 880908bbbc982569bb72df6af995c379215dcd28 Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 13:59:45 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E6=9C=80=E7=BB=88=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/weather_floating_widget.py | 203 ++++++++++++------------------ src/ui/word_style_ui.py | 4 +- src/word_main_window.py | 9 ++ 3 files changed, 92 insertions(+), 124 deletions(-) diff --git a/src/ui/weather_floating_widget.py b/src/ui/weather_floating_widget.py index bb373de..771bde8 100644 --- a/src/ui/weather_floating_widget.py +++ b/src/ui/weather_floating_widget.py @@ -39,7 +39,7 @@ class WeatherFloatingWidget(QDialog): """设置UI界面""" # 设置窗口属性 self.setWindowTitle("天气") - self.setFixedSize(360, 280) # 调整窗口尺寸使其更紧凑 + self.setFixedSize(320, 240) # 调整窗口尺寸使其更紧凑 # 创建主框架,用于实现圆角和阴影效果 self.main_frame = QFrame() @@ -52,25 +52,22 @@ class WeatherFloatingWidget(QDialog): # 内容布局 content_layout = QVBoxLayout(self.main_frame) - content_layout.setContentsMargins(10, 10, 10, 10) # 减小内边距使布局更紧凑 - content_layout.setSpacing(6) # 减小间距使布局更紧凑 - - # 设置最小尺寸策略 - self.main_frame.setMinimumSize(380, 300) + content_layout.setContentsMargins(12, 12, 12, 12) # 优化内边距 + content_layout.setSpacing(8) # 优化间距 # 标题栏 title_layout = QHBoxLayout() + title_layout.setContentsMargins(0, 0, 0, 0) + title_layout.setSpacing(0) self.title_label = QLabel("天气信息") self.title_label.setFont(QFont("Arial", 12, QFont.Bold)) title_layout.addWidget(self.title_label) title_layout.addStretch() - # 添加一个小的固定空间,使关闭按钮向左移动 - title_layout.addSpacing(25) # 向左移动25个单位 - # 关闭按钮 + # 关闭按钮 - 修复被遮挡问题 self.close_btn = QPushButton("×") - self.close_btn.setFixedSize(20, 20) + self.close_btn.setFixedSize(24, 24) self.close_btn.setObjectName("closeButton") title_layout.addWidget(self.close_btn) @@ -80,31 +77,32 @@ class WeatherFloatingWidget(QDialog): separator = QFrame() separator.setFrameShape(QFrame.HLine) separator.setObjectName("separator") + separator.setFixedHeight(1) content_layout.addWidget(separator) # 天气图标和温度显示区域 weather_display_layout = QHBoxLayout() - weather_display_layout.setSpacing(5) # 减小间距使布局更紧凑 - weather_display_layout.setContentsMargins(2, 2, 2, 2) # 减小内边距 + weather_display_layout.setSpacing(10) + weather_display_layout.setContentsMargins(0, 0, 0, 0) self.weather_icon_label = QLabel("🌞") - self.weather_icon_label.setFont(QFont("Arial", 24)) # 稍微减小字体大小 + self.weather_icon_label.setFont(QFont("Arial", 28)) self.weather_icon_label.setAlignment(Qt.AlignCenter) - self.weather_icon_label.setFixedSize(50, 50) # 减小尺寸 + self.weather_icon_label.setFixedSize(60, 60) weather_display_layout.addWidget(self.weather_icon_label) # 温度和城市信息 temp_city_layout = QVBoxLayout() - temp_city_layout.setSpacing(4) # 减小间距使布局更紧凑 + temp_city_layout.setSpacing(4) temp_city_layout.setContentsMargins(0, 0, 0, 0) self.temperature_label = QLabel("25°C") - self.temperature_label.setFont(QFont("Arial", 18, QFont.Bold)) # 稍微减小字体大小 + self.temperature_label.setFont(QFont("Arial", 20, QFont.Bold)) self.temperature_label.setObjectName("temperatureLabel") temp_city_layout.addWidget(self.temperature_label) self.city_label = QLabel("北京") - self.city_label.setFont(QFont("Arial", 11)) # 稍微减小字体大小 + self.city_label.setFont(QFont("Arial", 12)) self.city_label.setObjectName("cityLabel") temp_city_layout.addWidget(self.city_label) @@ -115,79 +113,88 @@ class WeatherFloatingWidget(QDialog): # 天气描述 self.weather_desc_label = QLabel("晴天") - self.weather_desc_label.setFont(QFont("Arial", 11)) # 稍微减小字体大小 + self.weather_desc_label.setFont(QFont("Arial", 12)) self.weather_desc_label.setObjectName("weatherDescLabel") self.weather_desc_label.setAlignment(Qt.AlignCenter) content_layout.addWidget(self.weather_desc_label) # 详细信息(湿度、风速) details_layout = QHBoxLayout() - details_layout.setSpacing(6) # 减小间距使布局更紧凑 - details_layout.setContentsMargins(2, 2, 2, 2) # 减小内边距 + details_layout.setSpacing(12) + details_layout.setContentsMargins(0, 0, 0, 0) self.humidity_label = QLabel("湿度: 45%") - self.humidity_label.setFont(QFont("Arial", 10)) # 稍微减小字体大小 + self.humidity_label.setFont(QFont("Arial", 11)) self.humidity_label.setObjectName("detailLabel") details_layout.addWidget(self.humidity_label) self.wind_label = QLabel("风速: 2级") - self.wind_label.setFont(QFont("Arial", 10)) # 稍微减小字体大小 + self.wind_label.setFont(QFont("Arial", 11)) self.wind_label.setObjectName("detailLabel") details_layout.addWidget(self.wind_label) + details_layout.addStretch() content_layout.addLayout(details_layout) - # 城市选择区域 - city_layout = QHBoxLayout() - city_layout.setSpacing(6) # 减小间距使布局更紧凑 - city_layout.setContentsMargins(0, 0, 0, 0) + # 添加弹性空间 + content_layout.addStretch() + + # 城市选择和按钮区域 + control_layout = QHBoxLayout() + control_layout.setSpacing(8) + control_layout.setContentsMargins(0, 0, 0, 0) + # 城市选择下拉框 self.city_combo = QComboBox() - self.city_combo.setObjectName("cityCombo") - # 添加所有省会城市,与主窗口保持一致 self.city_combo.addItems([ - '自动定位', - '北京', '上海', '广州', '深圳', '杭州', '南京', '武汉', '成都', '西安', # 一线城市 - '天津', '重庆', '苏州', '青岛', '大连', '宁波', '厦门', '无锡', '佛山', # 新一线城市 - '石家庄', '太原', '呼和浩特', '沈阳', '长春', '哈尔滨', # 东北华北 - '合肥', '福州', '南昌', '济南', '郑州', '长沙', '南宁', '海口', # 华东华中华南 - '贵阳', '昆明', '拉萨', '兰州', '西宁', '银川', '乌鲁木齐' # 西南西北 + '自动定位', + # 直辖市 + '北京', '上海', '天津', '重庆', + # 省会城市 + '石家庄', '太原', '呼和浩特', '沈阳', '长春', '哈尔滨', '南京', '杭州', '合肥', '福州', + '南昌', '济南', '郑州', '武汉', '长沙', '广州', '南宁', '海口', '成都', '贵阳', + '昆明', '拉萨', '西安', '兰州', '西宁', '银川', '乌鲁木齐', + # 特别行政区 + '香港', '澳门', + # 台湾省主要城市 + '台北', '高雄', + # 主要地级市和经济中心城市 + '深圳', '青岛', '大连', '宁波', '厦门', '苏州', '无锡', '佛山', '东莞', '中山', + '泉州', '南通', '常州', '徐州', '温州', '烟台', '威海', '嘉兴', '湖州', '绍兴', + '金华', '台州', '芜湖', '蚌埠', '安庆', '阜阳', '九江', '赣州', '吉安', '上饶', + '淄博', '枣庄', '东营', '潍坊', '济宁', '泰安', '威海', '日照', '临沂', '德州', + '聊城', '滨州', '菏泽', '洛阳', '平顶山', '安阳', '鹤壁', '新乡', '焦作', '濮阳', + '许昌', '漯河', '三门峡', '商丘', '信阳', '周口', '驻马店', '黄石', '十堰', '宜昌', + '襄阳', '鄂州', '荆门', '孝感', '荆州', '黄冈', '咸宁', '随州', '株洲', '湘潭', + '衡阳', '邵阳', '岳阳', '常德', '张家界', '益阳', '郴州', '永州', '怀化', '娄底', + '韶关', '珠海', '汕头', '惠州', '江门', '湛江', '茂名', '肇庆', '梅州', '汕尾', + '河源', '阳江', '清远', '潮州', '揭阳', '云浮', '柳州', '桂林', '梧州', '北海', + '防城港', '钦州', '贵港', '玉林', '百色', '贺州', '河池', '来宾', '崇左', '三亚', + '儋州', '五指山', '琼海', '文昌', '万宁', '东方' ]) - self.city_combo.setFixedWidth(100) # 减小城市选择框宽度使布局更紧凑 - city_layout.addWidget(self.city_combo) - - city_layout.addStretch() - - content_layout.addLayout(city_layout) - - # 按钮区域 - button_layout = QHBoxLayout() - button_layout.setSpacing(6) # 减小间距使布局更紧凑 - button_layout.setContentsMargins(0, 0, 0, 0) + self.city_combo.setCurrentText('自动定位') + self.city_combo.currentTextChanged.connect(self.on_city_changed) + control_layout.addWidget(self.city_combo) self.refresh_btn = QPushButton("刷新") self.refresh_btn.setObjectName("refreshButton") - self.refresh_btn.setFixedHeight(26) # 减小按钮高度 - button_layout.addWidget(self.refresh_btn) + self.refresh_btn.setFixedHeight(28) + control_layout.addWidget(self.refresh_btn) - button_layout.addStretch() + control_layout.addStretch() self.detail_btn = QPushButton("详情") self.detail_btn.setObjectName("detailButton") - self.detail_btn.setFixedHeight(26) # 减小按钮高度 - button_layout.addWidget(self.detail_btn) + self.detail_btn.setFixedHeight(28) + control_layout.addWidget(self.detail_btn) - content_layout.addLayout(button_layout) - - # 添加弹性空间 - content_layout.addStretch() + content_layout.addLayout(control_layout) def setup_connections(self): """设置信号连接""" self.close_btn.clicked.connect(self.close_window) self.refresh_btn.clicked.connect(self.on_refresh_clicked) self.detail_btn.clicked.connect(self.show_detailed_weather) - self.city_combo.currentTextChanged.connect(self.on_city_changed) def setup_theme(self): """设置主题""" @@ -208,7 +215,7 @@ class WeatherFloatingWidget(QDialog): QFrame#mainFrame {{ background-color: {colors['surface']}; border: 1px solid {colors['border']}; - border-radius: 10px; + border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); }} QLabel {{ @@ -219,27 +226,27 @@ class WeatherFloatingWidget(QDialog): }} QLabel#temperatureLabel {{ color: {colors['accent']}; - font-size: 20px; + font-size: 22px; font-weight: bold; - padding: 6px 8px; - margin: 3px; + padding: 0px 8px; + margin: 0px 3px; }} QLabel#cityLabel {{ color: {colors['text_secondary']}; - font-size: 12px; + font-size: 13px; padding: 4px 6px; margin: 2px; }} QLabel#weatherDescLabel {{ color: {colors['text']}; - font-size: 12px; + font-size: 13px; font-weight: 500; padding: 4px 6px; margin: 2px; }} QLabel#detailLabel {{ color: {colors['text_secondary']}; - font-size: 11px; + font-size: 12px; padding: 4px 6px; margin: 2px; }} @@ -266,35 +273,12 @@ class WeatherFloatingWidget(QDialog): border: none; border-radius: 6px; padding: 6px 16px; - font-size: 11px; + font-size: 12px; font-weight: 500; }} QPushButton#refreshButton:hover, QPushButton#detailButton:hover {{ background-color: {colors['accent_hover']}; }} - QComboBox#cityCombo {{ - background-color: {colors['surface']}; - color: {colors['text']}; - border: 1px solid {colors['border']}; - border-radius: 6px; - padding: 4px 7px; - font-size: 11px; - font-weight: 500; - min-height: 24px; - }} - QComboBox#cityCombo:hover {{ - border-color: {colors['accent']}; - }} - QComboBox#cityCombo::drop-down {{ - border: none; - width: 14px; - }} - QComboBox#cityCombo::down-arrow {{ - image: none; - border-left: 2px solid transparent; - border-right: 2px solid transparent; - border-top: 5px solid {colors['text']}; - }} """) else: # 浅色主题样式 - 与每日谏言悬浮窗口保持一致 @@ -302,7 +286,7 @@ class WeatherFloatingWidget(QDialog): QFrame#mainFrame {{ background-color: {colors['surface']}; border: 1px solid {colors['border']}; - border-radius: 10px; + border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); }} QLabel {{ @@ -313,27 +297,27 @@ class WeatherFloatingWidget(QDialog): }} QLabel#temperatureLabel {{ color: {colors['accent']}; - font-size: 20px; + font-size: 22px; font-weight: bold; - padding: 6px 8px; - margin: 3px; + padding: 0px 8px; + margin: 0px 3px; }} QLabel#cityLabel {{ color: {colors['text_secondary']}; - font-size: 12px; + font-size: 13px; padding: 4px 6px; margin: 2px; }} QLabel#weatherDescLabel {{ color: {colors['text']}; - font-size: 12px; + font-size: 13px; font-weight: 500; padding: 4px 6px; margin: 2px; }} QLabel#detailLabel {{ color: {colors['text_secondary']}; - font-size: 11px; + font-size: 12px; padding: 4px 6px; margin: 2px; }} @@ -360,35 +344,12 @@ class WeatherFloatingWidget(QDialog): border: none; border-radius: 6px; padding: 6px 16px; - font-size: 11px; + font-size: 12px; font-weight: 500; }} QPushButton#refreshButton:hover, QPushButton#detailButton:hover {{ background-color: {colors['accent_hover']}; }} - QComboBox#cityCombo {{ - background-color: {colors['surface']}; - color: {colors['text']}; - border: 1px solid {colors['border']}; - border-radius: 6px; - padding: 4px 7px; - font-size: 11px; - font-weight: 500; - min-height: 24px; - }} - QComboBox#cityCombo:hover {{ - border-color: {colors['accent']}; - }} - QComboBox#cityCombo::drop-down {{ - border: none; - width: 14px; - }} - QComboBox#cityCombo::down-arrow {{ - image: none; - border-left: 2px solid transparent; - border-right: 2px solid transparent; - border-top: 5px solid {colors['text']}; - }} """) def on_theme_changed(self, is_dark): @@ -496,12 +457,8 @@ class WeatherFloatingWidget(QDialog): def set_current_city(self, city_name): """设置当前城市""" - # 阻止信号发射,避免循环调用 - self.city_combo.blockSignals(True) - index = self.city_combo.findText(city_name) - if index >= 0: - self.city_combo.setCurrentIndex(index) - self.city_combo.blockSignals(False) + if hasattr(self, 'city_combo'): + self.city_combo.setCurrentText(city_name) def close_window(self): """关闭窗口 - 只是隐藏而不是销毁""" diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index 65e0525..be39837 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -880,7 +880,9 @@ class WordRibbon(QFrame): def on_city_changed(self, city): """城市选择变化处理""" - pass + # 通知主窗口城市已更改 + if hasattr(self.parent(), 'on_city_changed'): + self.parent().on_city_changed(city) def load_daily_quote(self): """加载每日一言""" diff --git a/src/word_main_window.py b/src/word_main_window.py index 89b018b..452a2b8 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -462,6 +462,12 @@ class WordStyleMainWindow(QMainWindow): def on_city_changed(self, city): """城市选择变化处理""" print(f"城市选择变化: {city}") + # 同步Ribbon和天气悬浮窗口的城市选择 + if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'city_combo'): + self.ribbon.city_combo.setCurrentText(city) + if hasattr(self, 'weather_floating_widget') and hasattr(self.weather_floating_widget, 'city_combo'): + self.weather_floating_widget.city_combo.setCurrentText(city) + if city == '自动定位': self.refresh_weather() # 重新自动定位 else: @@ -1823,6 +1829,9 @@ class WordStyleMainWindow(QMainWindow): # 如果有天气数据,更新显示 if hasattr(self, 'current_weather_data') and self.current_weather_data: self.weather_floating_widget.update_weather(self.current_weather_data) + # 刷新天气数据以确保显示最新信息 + else: + self.refresh_weather() def on_weather_floating_closed(self): """天气悬浮窗口关闭时的处理""" From 5e7f48cc1be337e78baf5c8e7b2764cf9b97c337 Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 20:08:43 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E8=A1=A8=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/word_style_ui.py | 67 ++++++++++++++++++++++--- src/word_main_window.py | 106 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 8 deletions(-) diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index be39837..8e832db 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -238,18 +238,19 @@ class WordRibbon(QFrame): preview_layout.setSpacing(8) style_items = [ - ("正文", "font-size:14px;"), - ("无间隔", "font-size:14px;"), - ("标题 1", "font-size:22px; font-weight:bold; color:#2E75B6;"), - ("标题 2", "font-size:18px; color:#2E75B6;"), - ("标题 3", "font-size:16px; font-weight:bold;"), - ("副标题", "font-size:14px; font-style:italic; color:#555;"), - ("强调", "font-size:14px; color:#C0504D;"), + ("正文", "font-size:14px;", "body_text_preview_btn"), + ("无间隔", "font-size:14px;", "no_spacing_preview_btn"), + ("标题 1", "font-size:22px; font-weight:bold; color:#2E75B6;", "heading1_preview_btn"), + ("标题 2", "font-size:18px; color:#2E75B6;", "heading2_preview_btn"), + ("标题 3", "font-size:16px; font-weight:bold;", "heading3_preview_btn"), + ("副标题", "font-size:14px; font-style:italic; color:#555;", "subtitle_preview_btn"), + ("强调", "font-size:14px; color:#C0504D;", "emphasis_preview_btn"), ] - for text, style in style_items: + for text, style, obj_name in style_items: btn = QPushButton(text) btn.setFixedSize(95, 60) + btn.setObjectName(obj_name) btn.setStyleSheet(f""" QPushButton {{ background: white; @@ -264,6 +265,9 @@ class WordRibbon(QFrame): }} """) preview_layout.addWidget(btn) + + # 将按钮保存为实例属性,以便主窗口可以连接信号 + setattr(self, obj_name, btn) preview_layout.addStretch() @@ -300,6 +304,9 @@ class WordRibbon(QFrame): self.update_combo_styles(is_dark) self.update_font_button_styles(is_dark) + # 更新样式预览按钮样式 + self.update_style_preview_buttons(is_dark) + # 更新天气组件样式 if hasattr(self, 'weather_icon_label') and self.weather_icon_label is not None: self.weather_icon_label.setStyleSheet(f""" @@ -397,6 +404,50 @@ class WordRibbon(QFrame): # 更新字体工具栏按钮样式 self.update_font_button_styles(is_dark) + def update_style_preview_buttons(self, is_dark): + """更新样式预览按钮样式""" + colors = theme_manager.get_current_theme_colors() + + # 样式预览按钮配置 + style_items = [ + ("正文", "font-size:14px;", "body_text_preview_btn"), + ("无间隔", "font-size:14px;", "no_spacing_preview_btn"), + ("标题 1", "font-size:22px; font-weight:bold; color:#2E75B6;", "heading1_preview_btn"), + ("标题 2", "font-size:18px; color:#2E75B6;", "heading2_preview_btn"), + ("标题 3", "font-size:16px; font-weight:bold;", "heading3_preview_btn"), + ("副标题", "font-size:14px; font-style:italic; color:#555;", "subtitle_preview_btn"), + ("强调", "font-size:14px; color:#C0504D;", "emphasis_preview_btn"), + ] + + for text, style, obj_name in style_items: + if hasattr(self, obj_name): + btn = getattr(self, obj_name) + # 根据主题调整颜色 + if is_dark: + # 黑色模式下的颜色调整 + if "color:#2E75B6" in style: + style = style.replace("color:#2E75B6", "color:#5B9BD5") + elif "color:#555" in style: + style = style.replace("color:#555", "color:#999") + elif "color:#C0504D" in style: + style = style.replace("color:#C0504D", "color:#E74C3C") + + btn.setStyleSheet(f""" + QPushButton {{ + background-color: {colors['surface']}; + border: 1px solid {colors['border']}; + border-radius: 3px; + text-align: left; + padding: 5px; + color: {colors['text']}; + {style} + }} + QPushButton:hover {{ + border: 1px solid {colors['accent']}; + background-color: {colors['surface_hover']}; + }} + """) + def update_font_button_styles(self, is_dark): """更新字体工具栏按钮样式""" colors = theme_manager.get_current_theme_colors() diff --git a/src/word_main_window.py b/src/word_main_window.py index 452a2b8..8e66b19 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -974,6 +974,22 @@ class WordStyleMainWindow(QMainWindow): if hasattr(self.ribbon, 'body_text_btn'): self.ribbon.body_text_btn.clicked.connect(self.on_body_text_clicked) + # 样式预览按钮信号 + if hasattr(self.ribbon, 'body_text_preview_btn'): + self.ribbon.body_text_preview_btn.clicked.connect(self.on_body_text_clicked) + if hasattr(self.ribbon, 'no_spacing_preview_btn'): + self.ribbon.no_spacing_preview_btn.clicked.connect(self.on_no_spacing_clicked) + if hasattr(self.ribbon, 'heading1_preview_btn'): + self.ribbon.heading1_preview_btn.clicked.connect(self.on_heading1_clicked) + if hasattr(self.ribbon, 'heading2_preview_btn'): + self.ribbon.heading2_preview_btn.clicked.connect(self.on_heading2_clicked) + if hasattr(self.ribbon, 'heading3_preview_btn'): + self.ribbon.heading3_preview_btn.clicked.connect(self.on_heading3_clicked) + if hasattr(self.ribbon, 'subtitle_preview_btn'): + self.ribbon.subtitle_preview_btn.clicked.connect(self.on_subtitle_clicked) + if hasattr(self.ribbon, 'emphasis_preview_btn'): + self.ribbon.emphasis_preview_btn.clicked.connect(self.on_emphasis_clicked) + # 查找和替换按钮信号 if hasattr(self.ribbon, 'find_btn'): self.ribbon.find_btn.clicked.connect(self.show_find_dialog) @@ -1482,6 +1498,18 @@ class WordStyleMainWindow(QMainWindow): """正文按钮点击处理""" self.apply_body_text_style() + def on_subtitle_clicked(self): + """副标题按钮点击处理""" + self.apply_subtitle_style() + + def on_emphasis_clicked(self): + """强调按钮点击处理""" + self.apply_emphasis_style() + + def on_no_spacing_clicked(self): + """无间隔按钮点击处理""" + self.apply_no_spacing_style() + def on_align_left_clicked(self): """左对齐按钮点击处理""" self.apply_alignment(Qt.AlignLeft) @@ -1589,6 +1617,84 @@ class WordStyleMainWindow(QMainWindow): self.text_edit.setCurrentCharFormat(char_format) self.text_edit.textCursor().setBlockFormat(block_format) + def apply_subtitle_style(self): + """应用副标题样式""" + cursor = self.text_edit.textCursor() + + # 创建字符格式 + char_format = QTextCharFormat() + char_format.setFontPointSize(16) # 副标题字号 + char_format.setFontWeight(QFont.Bold) # 加粗 + char_format.setFontItalic(True) # 斜体 + + # 创建块格式(段落格式) + block_format = QTextBlockFormat() + block_format.setTopMargin(12) + block_format.setBottomMargin(8) + + # 应用格式 + if cursor.hasSelection(): + # 如果有选中文本,只更改选中文本的格式 + cursor.mergeCharFormat(char_format) + else: + # 如果没有选中文本,更改当前段落的格式 + cursor.setBlockFormat(block_format) + cursor.mergeCharFormat(char_format) + # 将光标移动到段落末尾并添加换行 + cursor.movePosition(QTextCursor.EndOfBlock) + cursor.insertText("\n") + + # 设置文本编辑器的默认格式 + self.text_edit.setCurrentCharFormat(char_format) + self.text_edit.textCursor().setBlockFormat(block_format) + + def apply_emphasis_style(self): + """应用强调样式""" + cursor = self.text_edit.textCursor() + + # 创建字符格式 + char_format = QTextCharFormat() + char_format.setFontPointSize(12) # 正文字号 + char_format.setFontWeight(QFont.Bold) # 加粗 + char_format.setFontItalic(True) # 斜体 + char_format.setForeground(QColor(0, 0, 128)) # 深蓝色 + + # 应用格式(只影响字符格式,不影响段落格式) + if cursor.hasSelection(): + # 如果有选中文本,只更改选中文本的格式 + cursor.mergeCharFormat(char_format) + else: + # 如果没有选中文本,设置默认字符格式供后续输入使用 + self.text_edit.setCurrentCharFormat(char_format) + + def apply_no_spacing_style(self): + """应用无间隔样式""" + cursor = self.text_edit.textCursor() + + # 创建字符格式 + char_format = QTextCharFormat() + char_format.setFontPointSize(12) # 正文字号 + char_format.setFontWeight(QFont.Normal) # 正常粗细 + + # 创建块格式(段落格式) + block_format = QTextBlockFormat() + block_format.setTopMargin(0) # 无上边距 + block_format.setBottomMargin(0) # 无下边距 + block_format.setLineHeight(100, QTextBlockFormat.SingleHeight) # 行高100%,无额外间距 + + # 应用格式 + if cursor.hasSelection(): + # 如果有选中文本,只更改选中文本的格式 + cursor.mergeCharFormat(char_format) + else: + # 如果没有选中文本,更改当前段落的格式 + cursor.setBlockFormat(block_format) + cursor.mergeCharFormat(char_format) + + # 设置文本编辑器的默认格式 + self.text_edit.setCurrentCharFormat(char_format) + self.text_edit.textCursor().setBlockFormat(block_format) + def apply_alignment(self, alignment): """应用段落对齐方式""" cursor = self.text_edit.textCursor() From 5651226e57b6fc810167f26247796c40d830eb33 Mon Sep 17 00:00:00 2001 From: Maziang <929110464@qq.com> Date: Sun, 23 Nov 2025 20:17:02 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E6=AE=B5=E8=90=BD=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/word_style_ui.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index 8e832db..0957101 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -1069,8 +1069,10 @@ class WordRibbon(QFrame): btn.setFixedSize(32, 28) # 单个字符(如B、I、U) elif len(text) <= 2: btn.setFixedSize(48, 28) # 两个字符(如"居中") + elif len(text) <= 3: + btn.setFixedSize(64, 28) # 三个字符(如"左对齐") else: - btn.setFixedSize(64, 28) # 三个字符及以上(如"左对齐"、"两端对齐") + btn.setFixedSize(80, 28) # 四个字符及以上(如"两端对齐") # 连接主题切换信号以动态更新样式 theme_manager.theme_changed.connect(lambda: self._update_toggle_button_style(btn))