diff --git a/src/main.py b/src/main.py index a1202a6..cd032f3 100644 --- a/src/main.py +++ b/src/main.py @@ -29,6 +29,7 @@ from services.network_service import NetworkService from file_manager.file_operations import FileManager from learning_mode_window import LearningModeWindow from ui.theme_manager import ThemeManager +from ui.ai_chat_panel import AIChatPanel class MarkTextEditor(QPlainTextEdit): @@ -446,8 +447,13 @@ class MarkTextMainWindow(QMainWindow): editor_container.setLayout(editor_layout) splitter.addWidget(editor_container) + # AI对话面板 + self.ai_chat_panel = AIChatPanel(self) + self.ai_chat_panel.setMinimumWidth(320) + splitter.addWidget(self.ai_chat_panel) + # 设置分割器比例 - splitter.setSizes([250, 950]) + splitter.setSizes([250, 700, 300]) main_layout.addWidget(splitter) central_widget.setLayout(main_layout) @@ -1148,7 +1154,7 @@ class MarkTextMainWindow(QMainWindow): QMessageBox.warning(self, "提示", "请先打开一个文档") def show_weather_info(self): - """显示天气信息 - 优化错误处理""" + """显示天气信息 - 包含生活提示的详细信息""" if not self.network_service: QMessageBox.warning(self, "提示", "网络服务正在初始化中,请稍后再试") return @@ -1156,8 +1162,21 @@ class MarkTextMainWindow(QMainWindow): try: weather_info = self.network_service.get_weather_info() if weather_info: + # 基础天气信息 weather_text = f"{weather_info['city']}: {weather_info['temperature']}°C, {weather_info['description']}" - QMessageBox.information(self, "天气信息", weather_text) + + # 添加详细信息 + weather_text += f"\n湿度: {weather_info['humidity']}%" + weather_text += f"\n风速: {weather_info['wind_speed']}m/s" + + # 添加生活提示 + lifetips = weather_info.get('lifetips', []) + if lifetips: + weather_text += "\n\n🌟 生活提示:" + for tip in lifetips: + weather_text += f"\n• {tip}" + + QMessageBox.information(self, "天气详细信息", weather_text) else: QMessageBox.warning(self, "提示", "无法获取天气信息") except Exception as e: @@ -1179,7 +1198,7 @@ class MarkTextMainWindow(QMainWindow): QMessageBox.critical(self, "错误", f"获取名言信息失败: {str(e)}") def insert_weather_to_editor(self): - """将天气信息插入到编辑器 - 优化错误处理""" + """将天气信息插入到编辑器 - 包含生活提示的详细信息""" if not self.network_service: QMessageBox.warning(self, "提示", "网络服务正在初始化中,请稍后再试") return @@ -1189,7 +1208,17 @@ class MarkTextMainWindow(QMainWindow): try: weather_info = self.network_service.get_weather_info() if weather_info: - weather_text = f"\n\n🌤 今日天气: {weather_info['city']} {weather_info['temperature']}°C, {weather_info['description']}\n\n" + weather_text = f"\n\n🌤 今日天气: {weather_info['city']} {weather_info['temperature']}°C, {weather_info['description']}\n" + weather_text += f"湿度: {weather_info['humidity']}% | 风速: {weather_info['wind_speed']}m/s\n\n" + + # 添加生活提示 + lifetips = weather_info.get('lifetips', []) + if lifetips: + weather_text += "🌟 生活提示:\n" + for tip in lifetips: + weather_text += f"• {tip}\n" + weather_text += "\n" + cursor = editor.textCursor() cursor.insertText(weather_text) else: diff --git a/src/services/network_service.py b/src/services/network_service.py index a466e19..496c1b4 100644 --- a/src/services/network_service.py +++ b/src/services/network_service.py @@ -2,6 +2,7 @@ import requests import json import os +import time from typing import Optional, Dict, Any class NetworkService: @@ -11,6 +12,36 @@ class NetworkService: self.api_key = None self.cache = {} self.session = requests.Session() + # 天气缓存相关属性 + self._cached_weather_data = None # 缓存的天气数据 + self._cached_location = None # 缓存的定位信息 + self._weather_cache_timestamp = None # 缓存时间戳 + + def get_cached_weather_data(self): + """获取缓存的天气数据""" + return self._cached_weather_data + + def get_cached_location(self): + """获取缓存的定位信息""" + return self._cached_location + + def set_weather_cache(self, weather_data, location): + """设置天气缓存""" + self._cached_weather_data = weather_data + self._cached_location = location + self._weather_cache_timestamp = time.time() + + def clear_weather_cache(self): + """清除天气缓存""" + self._cached_weather_data = None + self._cached_location = None + self._weather_cache_timestamp = None + + def is_weather_cache_valid(self): + """检查天气缓存是否有效(30分钟内)""" + if self._weather_cache_timestamp is None: + return False + return (time.time() - self._weather_cache_timestamp) < 1800 # 30分钟 def get_user_ip(self): """获取用户IP地址 - 使用多个备用服务""" @@ -59,7 +90,13 @@ class NetworkService: "wind_speed": 3.5 } - def get_weather_info(self) -> Optional[Dict[str, Any]]: + def get_weather_info(self, use_cache: bool = True) -> Optional[Dict[str, Any]]: + """获取天气信息,支持缓存机制""" + + # 如果启用缓存且缓存有效,直接返回缓存数据 + if use_cache and self.is_weather_cache_valid(): + print("使用缓存的天气数据") + return self._cached_weather_data # 实现天气信息获取逻辑 # 1. 获取用户IP地址 - 使用多个备用服务 @@ -91,6 +128,14 @@ class NetworkService: print("无法获取城市名称,使用默认天气数据") return self.get_default_weather() + # 保存定位信息到缓存 + self._cached_location = { + "ip": ip, + "city": city, + "country": location_data.get("country", "Unknown"), + "region": location_data.get("regionName", "Unknown") + } + # 3. 调用天气API获取天气数据 # 注意:这里使用OpenWeatherMap API作为示例,需要API密钥 # 在实际应用中,需要设置有效的API密钥 @@ -162,14 +207,47 @@ class NetworkService: current_weather = forecast[0] if forecast else {} weather_type = current_weather.get("type", "晴") + # 获取生活指数信息 + lifetips = [] + if current_weather: + # 从预报数据中提取生活提示 + ganmao = current_weather.get("ganmao", "") + if ganmao: + lifetips.append(f"感冒指数: {ganmao}") + + # 添加其他生活指数(基于天气类型推断) + if "雨" in weather_type: + lifetips.append("出行建议: 记得带伞") + elif "晴" in weather_type: + lifetips.append("出行建议: 适合户外活动") + elif "雪" in weather_type: + lifetips.append("出行建议: 注意防滑保暖") + elif "雾" in weather_type or "霾" in weather_type: + lifetips.append("健康提醒: 减少户外运动") + + # 温度相关建议 + temp = float(wendu) if wendu != "N/A" else 20 + if temp > 30: + lifetips.append("穿衣建议: 注意防暑降温") + elif temp < 5: + lifetips.append("穿衣建议: 注意保暖防寒") + elif temp < 15: + lifetips.append("穿衣建议: 适当添加衣物") + else: + lifetips.append("穿衣建议: 天气舒适") + formatted_weather = { "city": city, "temperature": float(wendu) if wendu != "N/A" else 20, "description": weather_type, "humidity": shidu.replace("%", "") if shidu != "N/A" else "60", - "wind_speed": "3.5" # 默认风速 + "wind_speed": "3.5", # 默认风速 + "lifetips": lifetips # 生活提示列表 } print(f"成功获取天气数据: {formatted_weather}") + + # 缓存天气数据 + self.set_weather_cache(formatted_weather, self._cached_location) return formatted_weather else: print(f"天气API返回错误状态: {weather_data.get('status')}") @@ -178,44 +256,60 @@ class NetworkService: print(f"获取免费天气数据时出错: {e}") # 如果以上都失败,返回默认数据 - print(f"使用默认天气数据 for {city}") - return { + default_weather = { "city": city, "temperature": 20, "description": "晴天", "humidity": 60, - "wind_speed": 3.5 + "wind_speed": 3.5, + "lifetips": [ + "穿衣建议: 天气舒适", + "出行建议: 适合户外活动", + "健康提醒: 保持良好心情" + ] } + print(f"使用默认天气数据 for {city}") + + # 缓存默认天气数据 + self.set_weather_cache(default_weather, self._cached_location) + return default_weather except Exception as e: print(f"获取天气信息时出错: {e}") return None def get_daily_quote(self) -> Optional[str]: - # 实现每日一句获取逻辑 - # 1. 调用名言API + # 实现每日一句获取逻辑 - 使用古诗词API try: - # 使用一个免费的名言API,禁用SSL验证以避免证书问题 - response = self.session.get("https://api.quotable.io/random", timeout=5, verify=False) + # 使用古诗词·一言API - 每次返回随机不同的诗词 + response = self.session.get("https://v1.jinrishici.com/all.json", timeout=5, verify=False) - # 2. 解析返回的名言数据 + # 2. 解析返回的古诗词数据 if response.status_code == 200: - quote_data = response.json() - content = quote_data.get("content", "") - author = quote_data.get("author", "") + poetry_data = response.json() + content = poetry_data.get('content', '') + author = poetry_data.get('author', '') + title = poetry_data.get('origin', '') - # 3. 格式化名言文本 - formatted_quote = f'"{content}" - {author}' + # 3. 格式化古诗词文本 + if content and author and title: + formatted_poetry = f"{content} — {author}《{title}》" + elif content and author: + formatted_poetry = f"{content} — {author}" + elif content: + formatted_poetry = content + else: + formatted_poetry = "暂无古诗词" - # 4. 返回名言字符串 - return formatted_quote + # 4. 返回古诗词字符串 + return formatted_poetry else: - # 如果API调用失败,返回默认名言 - return "书山有路勤为径,学海无涯苦作舟。" + # 如果API调用失败,返回默认古诗词 + return "山重水复疑无路,柳暗花明又一村。" except Exception as e: - print(f"获取每日一句时出错: {e}") - # 出错时返回默认名言 - return "书山有路勤为径,学海无涯苦作舟。" + print(f"获取古诗词时出错: {e}") + # 出错时返回默认古诗词 + return "山重水复疑无路,柳暗花明又一村。" def download_image(self, url: str) -> Optional[bytes]: diff --git a/src/word_main_window.py b/src/word_main_window.py index 8e66b19..f5d0b70 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -168,6 +168,10 @@ class WordStyleMainWindow(QMainWindow): # 初始化时刷新天气 self.refresh_weather() + + # 初始化天气缓存相关属性 + self.cached_weather_data = None + self.cached_location = None def init_theme(self): """初始化主题""" @@ -933,6 +937,9 @@ class WordStyleMainWindow(QMainWindow): self.quote_thread = QuoteFetchThread() self.quote_thread.quote_fetched.connect(self.update_quote_display) self.quote_thread.start() + + # 获取初始天气数据并缓存 + self.init_weather_data() def init_typing_logic(self): @@ -1777,6 +1784,48 @@ class WordStyleMainWindow(QMainWindow): self.current_weather_data = weather_data print(f"update_weather_display - 存储的current_weather_data包含life_tips: {self.current_weather_data.get('life_tips', [])}") + def init_weather_data(self): + """初始化天气数据,使用缓存机制""" + try: + print("初始化天气数据,使用缓存机制") + + # 尝试从网络服务获取缓存的天气数据 + cached_weather = self.network_service.get_cached_weather_data() + cached_location = self.network_service.get_cached_location() + + if cached_weather and cached_location: + print(f"使用缓存的天气数据: {cached_weather}") + print(f"使用缓存的定位数据: {cached_location}") + + # 格式化缓存数据 + formatted_data = { + 'city': cached_weather.get('city', cached_location.get('city', '未知城市')), + 'current': cached_weather.get('current', {}), + 'forecast': cached_weather.get('forecast', []), + 'life_tips': cached_weather.get('life_tips', []) + } + + # 更新显示 + self.update_weather_display(formatted_data) + # 同步更新天气悬浮窗口 + if hasattr(self, 'weather_floating_widget') and self.weather_floating_widget.isVisible(): + self.weather_floating_widget.update_weather(formatted_data) + + # 保存到本地缓存 + self.cached_weather_data = cached_weather + self.cached_location = cached_location + + self.status_bar.showMessage("使用缓存天气数据", 2000) + else: + print("没有缓存数据,使用网络获取") + # 没有缓存数据,使用网络获取 + self.refresh_weather() + + except Exception as e: + print(f"初始化天气数据失败: {e}") + # 如果缓存获取失败,回退到网络获取 + self.refresh_weather() + def refresh_weather(self): """手动刷新天气信息""" try: @@ -1796,10 +1845,10 @@ class WordStyleMainWindow(QMainWindow): print(f"刷新天气 - 当前选择的城市: {current_city}") if current_city == '自动定位': - # 使用自动定位 + # 使用自动定位,启用缓存 weather_data = self.weather_api.get_weather_data() else: - # 使用选中的城市 + # 使用选中的城市,启用缓存 weather_data = self.weather_api.get_weather_data(current_city) if weather_data: @@ -1816,6 +1865,12 @@ class WordStyleMainWindow(QMainWindow): # 同步更新天气悬浮窗口 if hasattr(self, 'weather_floating_widget') and self.weather_floating_widget.isVisible(): self.weather_floating_widget.update_weather(formatted_data) + + # 保存到网络服务缓存和本地缓存 + self.network_service.set_weather_cache(weather_data, self.network_service.get_cached_location()) + self.cached_weather_data = weather_data + self.cached_location = self.network_service.get_cached_location() + self.status_bar.showMessage("天气数据已刷新", 2000) else: self.status_bar.showMessage("天气数据刷新失败,请检查API密钥", 3000) @@ -1829,14 +1884,17 @@ class WordStyleMainWindow(QMainWindow): """显示详细天气信息对话框""" from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit - # 检查是否有天气数据 - if not hasattr(self, 'current_weather_data') or not self.current_weather_data: + # 首先尝试使用本地缓存的天气数据 + if hasattr(self, 'cached_weather_data') and self.cached_weather_data: + weather_data = self.cached_weather_data + print(f"详细天气对话框 - 使用本地缓存天气数据: {weather_data}") + elif hasattr(self, 'current_weather_data') and self.current_weather_data: + weather_data = self.current_weather_data + print(f"详细天气对话框 - 使用当前天气数据: {weather_data}") + else: QMessageBox.information(self, "附加工具", "暂无天气数据,请先刷新天气信息") return - weather_data = self.current_weather_data - print(f"详细天气对话框 - 天气数据: {weather_data}") - # 创建对话框 dialog = QDialog(self) dialog.setWindowTitle("详细天气") diff --git a/test_weather_lifetips.py b/test_weather_lifetips.py new file mode 100644 index 0000000..f1e77fb --- /dev/null +++ b/test_weather_lifetips.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +""" +测试天气生活提示功能 +""" +import sys +import os + +# 添加src目录到路径 +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +from services.network_service import NetworkService + +def test_weather_with_lifetips(): + """测试包含生活提示的天气功能""" + print("🌤 测试天气生活提示功能") + print("=" * 50) + + # 创建网络服务实例 + network_service = NetworkService() + + # 获取天气信息 + print("正在获取天气信息...") + weather_info = network_service.get_weather_info() + + if weather_info: + print(f"✅ 成功获取天气数据:") + print(f"城市: {weather_info['city']}") + print(f"温度: {weather_info['temperature']}°C") + print(f"天气: {weather_info['description']}") + print(f"湿度: {weather_info['humidity']}%") + print(f"风速: {weather_info['wind_speed']}m/s") + + # 显示生活提示 + lifetips = weather_info.get('lifetips', []) + if lifetips: + print(f"\n🌟 生活提示 ({len(lifetips)}条):") + for i, tip in enumerate(lifetips, 1): + print(f" {i}. {tip}") + else: + print("⚠️ 未获取到生活提示") + + # 模拟显示详细信息格式 + print(f"\n📋 详细信息显示格式:") + weather_text = f"{weather_info['city']}: {weather_info['temperature']}°C, {weather_info['description']}" + weather_text += f"\n湿度: {weather_info['humidity']}%" + weather_text += f"\n风速: {weather_info['wind_speed']}m/s" + + if lifetips: + weather_text += "\n\n🌟 生活提示:" + for tip in lifetips: + weather_text += f"\n• {tip}" + + print(weather_text) + + else: + print("❌ 获取天气信息失败") + + print("\n" + "=" * 50) + print("测试完成!") + +if __name__ == "__main__": + test_weather_with_lifetips() \ No newline at end of file