'
)
# 自动滚动到底部
@@ -455,37 +506,132 @@ 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:
+ for i, msg in enumerate(self.conversation_history):
sender = msg["sender"]
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;"
+ 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 = "#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;"
+ 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 = "#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;"
+ 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);"
+
+ # 格式化消息内容,处理换行和特殊字符
+ if message:
+ # 将换行符转换为
')
+ # 处理多个连续空格
+ formatted_message = formatted_message.replace(' ', ' ')
+ else:
+ formatted_message = "正在思考..." if is_streaming else ""
- # 格式化消息内容
- formatted_message = message.replace('\n', '
') if message else "正在思考..."
+ # 为每个消息添加唯一的ID便于调试
+ message_id = f"msg-{i}"
+ # 优化消息气泡样式 - 添加阴影、更好的边距和动画效果
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/ai_chat_panel.py b/src/ui/ai_chat_panel.py
new file mode 100644
index 0000000..7db7f25
--- /dev/null
+++ b/src/ui/ai_chat_panel.py
@@ -0,0 +1,535 @@
+# 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
+
+# 导入主题管理器
+try:
+ from .theme_manager import theme_manager
+except ImportError:
+ # 处理直接运行的情况
+ from ui.theme_manager import theme_manager
+
+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)
+
+ # 连接主题变化信号
+ theme_manager.theme_changed.connect(self.on_theme_changed)
+
+ 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.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)
+
+ # 分割线
+ 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: 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)
+ main_layout.addWidget(self.chat_display)
+
+ # 输入区域
+ input_layout = QVBoxLayout()
+ input_layout.setSpacing(6)
+
+ # 输入框
+ self.input_field = QLineEdit()
+ self.input_field.setPlaceholderText("输入您的问题或请求...")
+ self.input_field.setStyleSheet("""
+ QLineEdit {
+ background-color: #f9f9f9;
+ border: 1px solid #d0d0d0;
+ 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: 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(6)
+
+ # 发送按钮
+ self.send_btn = QPushButton("发送")
+ self.send_btn.setFont(QFont("微软雅黑", 9))
+ self.send_btn.setStyleSheet("""
+ QPushButton {
+ background-color: #0078d4;
+ color: white;
+ border: none;
+ border-radius: 6px;
+ padding: 8px 20px;
+ font-weight: 500;
+ font-size: 12px;
+ min-width: 70px;
+ }
+ QPushButton:hover {
+ background-color: #0063b1;
+ }
+ 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()
+ 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;
+ }
+
+ /* 深色主题 */
+ 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):
+ """发送用户消息"""
+ 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("微软雅黑", 11))
+
+ if sender == "用户":
+ # 根据主题设置用户消息颜色
+ if theme_manager.is_dark_theme():
+ char_format.setForeground(QColor("#4A90E2")) # 深色主题下的蓝色
+ else:
+ char_format.setForeground(QColor("#0078d4")) # 浅色主题下的蓝色
+ char_format.setFontWeight(60) # 中等粗体
+ prefix = "你: "
+ else: # AI助手
+ # 根据主题设置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()
+ 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)
+
+ # 插入前缀
+ cursor.insertText(prefix, char_format)
+
+ # 插入消息
+ cursor.insertText(message, char_format)
+
+ # 自动滚动到底部
+ 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("微软雅黑", 11))
+
+ if sender == "用户":
+ # 根据主题设置用户消息颜色
+ if theme_manager.is_dark_theme():
+ char_format.setForeground(QColor("#4A90E2")) # 深色主题下的蓝色
+ else:
+ char_format.setForeground(QColor("#0078d4")) # 浅色主题下的蓝色
+ char_format.setFontWeight(60) # 中等粗体
+ prefix = "你: "
+ else:
+ # 根据主题设置AI消息颜色
+ if theme_manager.is_dark_theme():
+ char_format.setForeground(QColor("#e0e0e0")) # 深色主题下的浅灰色
+ else:
+ char_format.setForeground(QColor("#000000")) # 浅色主题下的纯黑色,提高可读性
+ char_format.setFontWeight(50) # 正常粗体
+ prefix = "AI: "
+
+ # 插入分隔符
+ if self.chat_display.toPlainText():
+ cursor.insertText("\n")
+
+ # 插入时间戳
+ timestamp_format = QTextCharFormat()
+ 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)
+
+ # 插入前缀
+ 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()
+
+ 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
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/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..0957101 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()
@@ -880,7 +931,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):
"""加载每日一言"""
@@ -1016,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))
diff --git a/src/word_main_window.py b/src/word_main_window.py
index 28973f1..8e66b19 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
@@ -359,7 +360,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):
"""更新功能区样式"""
@@ -461,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:
@@ -572,82 +579,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)
@@ -660,18 +667,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)
@@ -679,56 +686,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)')
@@ -786,29 +793,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('小游戏')
@@ -819,7 +814,7 @@ class WordStyleMainWindow(QMainWindow):
games_menu.addAction(snake_game_action)
# 帮助菜单
- help_menu = menubar.addMenu('帮助(H)')
+ help_menu = menubar.addMenu('帮助')
# 关于
about_action = QAction('关于 MagicWord', self)
@@ -828,8 +823,12 @@ class WordStyleMainWindow(QMainWindow):
def create_document_area(self, main_layout):
- """创建文档编辑区域"""
+ """创建文档编辑区域和AI对话面板"""
+ # 创建水平分割器
+ splitter = QSplitter(Qt.Horizontal)
+
+ # ========== 左侧:文档编辑区域 ==========
# 创建滚动区域
from PyQt5.QtWidgets import QScrollArea
@@ -906,7 +905,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 +934,7 @@ class WordStyleMainWindow(QMainWindow):
self.quote_thread.quote_fetched.connect(self.update_quote_display)
self.quote_thread.start()
+
def init_typing_logic(self):
"""初始化打字逻辑"""
# 使用默认内容初始化打字逻辑
@@ -959,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)
@@ -1467,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)
@@ -1574,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()
@@ -1814,6 +1935,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):
"""天气悬浮窗口关闭时的处理"""