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