From 8e95a13f0b0df5e2140cb31743d043673d18f6a5 Mon Sep 17 00:00:00 2001 From: Horse861 <929110464@qq.com> Date: Mon, 20 Oct 2025 10:26:46 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=A4=A9=E6=B0=94API=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/word_style_ui.py | 312 +++++++++++++++++++++++++++++++++++++++- src/word_main_window.py | 15 +- 2 files changed, 322 insertions(+), 5 deletions(-) diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index 39a4b9f..ed3f2df 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -56,10 +56,17 @@ class WordRibbon(QFrame): weather_group = self.create_ribbon_group("天气") weather_layout = QVBoxLayout() - # 城市选择 + # 城市选择 - 添加所有省会城市 self.city_combo = QComboBox() - self.city_combo.setFixedWidth(100) - self.city_combo.addItems(['自动定位', '北京', '上海', '广州', '深圳', '杭州', '南京', '武汉', '成都', '西安']) + self.city_combo.setFixedWidth(120) # 增加宽度以显示完整城市名 + self.city_combo.addItems([ + '自动定位', + '北京', '上海', '广州', '深圳', '杭州', '南京', '武汉', '成都', '西安', # 一线城市 + '天津', '重庆', '苏州', '青岛', '大连', '宁波', '厦门', '无锡', '佛山', # 新一线城市 + '石家庄', '太原', '呼和浩特', '沈阳', '长春', '哈尔滨', # 东北华北 + '合肥', '福州', '南昌', '济南', '郑州', '长沙', '南宁', '海口', # 华东华中华南 + '贵阳', '昆明', '拉萨', '兰州', '西宁', '银川', '乌鲁木齐' # 西南西北 + ]) self.city_combo.setCurrentText('自动定位') self.city_combo.currentTextChanged.connect(self.on_city_changed) @@ -575,6 +582,24 @@ class WeatherAPI: results[city] = weather_data return results + def get_isp_info(self): + """获取ISP信息""" + try: + url = "http://ip-api.com/json/" + response = requests.get(url, timeout=5) + response.raise_for_status() + + data = response.json() + if data.get('status') == 'success': + isp = data.get('isp', '') + org = data.get('org', '') + as_info = data.get('as', '') + return f"{isp} {org} {as_info}".strip() + return None + except Exception as e: + print(f"获取ISP信息失败: {e}") + return None + def get_location_by_ip(self): """通过IP地址获取用户位置""" try: @@ -675,12 +700,159 @@ class WeatherAPI: city = self.get_location_by_ip() if city: print(f"通过IP定位成功: {city}") + + # 检查是否是教育网或特殊网络环境 + isp_info = self.get_isp_info() + if isp_info and ('教育网' in isp_info or 'CERNET' in isp_info or 'University' in isp_info): + print(f"检测到教育网环境: {isp_info}") + print("教育网IP定位可能不准确,建议手动选择城市") + # 教育网环境下,如果定位到北京,可能是IP分配问题 + if city.lower() in ['beijing', '北京', 'haidian', '海淀']: + print("提示:教育网环境下北京定位可能是网络出口导致的") + return {'city': city, 'note': '教育网环境,定位可能不准确', 'isp': isp_info} + + # 智能处理 - 如果是区级单位,映射到市级城市 + district_to_city_map = { + # 北京各区 + '海淀': '北京', + 'haidian': '北京', + '朝阳': '北京', + 'chaoyang': '北京', + '东城': '北京', + 'dongcheng': '北京', + '西城': '北京', + 'xicheng': '北京', + '丰台': '北京', + 'fengtai': '北京', + '石景山': '北京', + 'shijingshan': '北京', + '通州': '北京', + 'tongzhou': '北京', + '昌平': '北京', + 'changping': '北京', + '大兴': '北京', + 'daxing': '北京', + '怀柔': '北京', + 'huairou': '北京', + '平谷': '北京', + 'pinggu': '北京', + '门头沟': '北京', + 'mentougou': '北京', + '密云': '北京', + 'miyun': '北京', + '延庆': '北京', + 'yanqing': '北京', + '房山': '北京', + 'fangshan': '北京', + '顺义': '北京', + 'shunyi': '北京', + + # 天津各区 + '和平': '天津', + 'heping': '天津', + '河东': '天津', + 'hedong': '天津', + '河西': '天津', + 'hexi': '天津', + '南开': '天津', + 'nankai': '天津', + '河北': '天津', + 'hebei': '天津', + '红桥': '天津', + 'hongqiao': '天津', + '滨海新区': '天津', + 'binhaixinqu': '天津', + '东丽': '天津', + 'dongli': '天津', + '西青': '天津', + 'xiqing': '天津', + '津南': '天津', + 'jinnan': '天津', + '北辰': '天津', + 'beichen': '天津', + '武清': '天津', + 'wuqing': '天津', + '宝坻': '天津', + 'baodi': '天津', + '宁河': '天津', + 'ninghe': '天津', + '静海': '天津', + 'jinghai': '天津', + '蓟州': '天津', + 'jizhou': '天津', + + # 上海各区 + '黄浦': '上海', + 'huangpu': '上海', + '徐汇': '上海', + 'xuhui': '上海', + '长宁': '上海', + 'changning': '上海', + '静安': '上海', + 'jingan': '上海', + '普陀': '上海', + 'putuo': '上海', + '虹口': '上海', + 'hongkou': '上海', + '杨浦': '上海', + 'yangpu': '上海', + '闵行': '上海', + 'minhang': '上海', + '宝山': '上海', + 'baoshan': '上海', + '嘉定': '上海', + 'jiading': '上海', + '浦东': '上海', + 'pudong': '上海', + '金山': '上海', + 'jinshan': '上海', + '松江': '上海', + 'songjiang': '上海', + '青浦': '上海', + 'qingpu': '上海', + '奉贤': '上海', + 'fengxian': '上海', + '崇明': '上海', + 'chongming': '上海', + + # 广州各区 + '天河': '广州', + 'tianhe': '广州', + '越秀': '广州', + 'yuexiu': '广州', + '海珠': '广州', + 'haizhu': '广州', + '荔湾': '广州', + 'liwan': '广州', + '白云': '广州', + 'baiyun': '广州', + '黄埔': '广州', + 'huangpu': '广州', + '番禺': '广州', + 'panyu': '广州', + '花都': '广州', + 'huadu': '广州', + '南沙': '广州', + 'nansha': '广州', + '从化': '广州', + 'conghua': '广州', + '增城': '广州', + 'zengcheng': '广州' + } + + # 检查是否是区级单位,如果是则映射到市级城市 + city_lower = city.lower() + if city_lower in district_to_city_map: + mapped_city = district_to_city_map[city_lower] + print(f"智能映射: {city} -> {mapped_city}") + city = mapped_city + return {'city': city} # 如果IP定位失败,尝试其他方法 # 可以尝试使用浏览器的地理位置API,但这需要前端支持 - print("自动定位失败,使用默认城市") + print("自动定位失败,建议手动选择城市") return None except Exception as e: @@ -692,6 +864,138 @@ class WeatherAPI: try: # 处理英文城市名映射 original_city_name = city_name + + # 智能城市映射 - 将区级单位映射到市级城市 + district_to_city_map = { + # 北京各区 + '海淀': '北京', + '朝阳': '北京', + '东城': '北京', + '西城': '北京', + '丰台': '北京', + '石景山': '北京', + '通州': '北京', + '昌平': '北京', + '大兴': '北京', + '怀柔': '北京', + '平谷': '北京', + '门头沟': '北京', + '密云': '北京', + '延庆': '北京', + '房山': '北京', + '顺义': '北京', + 'haidian': '北京', + 'chaoyang': '北京', + 'dongcheng': '北京', + 'xicheng': '北京', + 'fengtai': '北京', + 'shijingshan': '北京', + 'tongzhou': '北京', + 'changping': '北京', + 'daxing': '北京', + 'huairou': '北京', + 'pinggu': '北京', + 'mentougou': '北京', + 'miyun': '北京', + 'yanqing': '北京', + 'fangshan': '北京', + 'shunyi': '北京', + + # 天津各区(统一映射到天津) + '和平': '天津', + 'heping': '天津', + '河东': '天津', + 'hedong': '天津', + '河西': '天津', + 'hexi': '天津', + '南开': '天津', + 'nankai': '天津', + '河北': '天津', + '红桥': '天津', + 'hongqiao': '天津', + '滨海新区': '天津', + 'binhaixinqu': '天津', + '东丽': '天津', + 'dongli': '天津', + '西青': '天津', + 'xiqing': '天津', + '津南': '天津', + 'jinnan': '天津', + '北辰': '天津', + 'beichen': '天津', + '武清': '天津', + 'wuqing': '天津', + '宝坻': '天津', + 'baodi': '天津', + '宁河': '天津', + 'ninghe': '天津', + '静海': '天津', + 'jinghai': '天津', + '蓟州': '天津', + 'jizhou': '天津', + '黄浦': '上海', + '徐汇': '上海', + '长宁': '上海', + '静安': '上海', + '普陀': '上海', + '虹口': '上海', + '杨浦': '上海', + '闵行': '上海', + '宝山': '上海', + '嘉定': '上海', + '浦东': '上海', + '金山': '上海', + '松江': '上海', + '青浦': '上海', + '奉贤': '上海', + '崇明': '上海', + 'huangpu': '上海', + 'xuhui': '上海', + 'changning': '上海', + 'jingan': '上海', + 'putuo': '上海', + 'hongkou': '上海', + 'yangpu': '上海', + 'minhang': '上海', + 'baoshan': '上海', + 'jiading': '上海', + 'pudong': '上海', + 'jinshan': '上海', + 'songjiang': '上海', + 'qingpu': '上海', + 'fengxian': '上海', + 'chongming': '上海', + '天河': '广州', + '越秀': '广州', + '海珠': '广州', + '荔湾': '广州', + '白云': '广州', + '黄埔': '广州', + '番禺': '广州', + '花都': '广州', + '南沙': '广州', + '从化': '广州', + '增城': '广州', + 'tianhe': '广州', + 'yuexiu': '广州', + 'haizhu': '广州', + 'liwan': '广州', + 'baiyun': '广州', + 'huangpu': '广州', + 'panyu': '广州', + 'huadu': '广州', + 'nansha': '广州', + 'conghua': '广州', + 'zengcheng': '广州' + } + + # 检查是否是区级单位,如果是则映射到市级城市 + city_lower = city_name.lower() + if city_lower in district_to_city_map: + mapped_city = district_to_city_map[city_lower] + print(f"智能映射: {city_name} -> {mapped_city}") + city_name = mapped_city + if city_name in self.city_id_map: # 如果是英文城市名,先映射到中文 mapped_name = self.city_id_map.get(city_name) diff --git a/src/word_main_window.py b/src/word_main_window.py index 930cd59..35646cf 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -143,7 +143,20 @@ class WordStyleMainWindow(QMainWindow): if current_city == '自动定位': # 使用自动定位 print("使用自动定位") - weather_data = self.weather_api.get_weather_data() + location_info = self.weather_api.get_current_location() + if location_info: + if 'note' in location_info: + # 检测到特殊网络环境 + print(f"网络环境提示: {location_info['note']}") + if '教育网' in location_info['note'] and location_info['city'].lower() in ['beijing', '北京', 'haidian', '海淀']: + print("建议:教育网环境下北京定位可能不准确,可手动选择天津") + # 可以选择提示用户手动选择,或者使用上次的定位 + # 使用定位到的城市获取天气 + actual_city = location_info.get('city', '北京') + weather_data = self.weather_api.get_weather_data(actual_city) + else: + # 定位失败,使用默认城市 + weather_data = self.weather_api.get_weather_data() else: # 使用选中的城市 print(f"使用选中的城市: {current_city}") -- 2.34.1 From c2efb8dd7f3e1a2ecd62d40af2bb110316479f62 Mon Sep 17 00:00:00 2001 From: Horse861 <929110464@qq.com> Date: Mon, 20 Oct 2025 10:46:00 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/word_style_ui.py | 154 +++++++++++++++++++++++++++++++--------- src/word_main_window.py | 79 +++++++++++++++++++++ 2 files changed, 201 insertions(+), 32 deletions(-) diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index ed3f2df..c263dda 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -13,6 +13,9 @@ from datetime import datetime class WordRibbonTab(QWidget): def __init__(self, parent=None): super().__init__(parent) + self.weather_group = None # 天气组件组 + self.weather_visible = False # 天气组件显示状态 + self.ribbon_layout = None # 功能区布局 self.setup_ui() def setup_ui(self): @@ -24,6 +27,11 @@ class WordRibbonTab(QWidget): class WordRibbon(QFrame): def __init__(self, parent=None): super().__init__(parent) + self.weather_visible = False # 天气组件显示状态 + self.weather_group = None # 天气组件组 + self.quote_visible = False # 每日一言组件显示状态 + self.quote_group = None # 每日一言组件组 + self.ribbon_layout = None # 功能区布局 self.setup_ui() def setup_ui(self): @@ -45,42 +53,16 @@ class WordRibbon(QFrame): } """) - ribbon_layout = QHBoxLayout() - ribbon_layout.setContentsMargins(10, 5, 10, 5) - ribbon_layout.setSpacing(15) + self.ribbon_layout = QHBoxLayout() # 保存为实例变量 + self.ribbon_layout.setContentsMargins(10, 5, 10, 5) + self.ribbon_layout.setSpacing(15) # 开始标签的内容(最常用的功能) - self.setup_home_tab(ribbon_layout) + self.setup_home_tab(self.ribbon_layout) - # 添加天气工具组 - weather_group = self.create_ribbon_group("天气") - weather_layout = QVBoxLayout() - - # 城市选择 - 添加所有省会城市 - self.city_combo = QComboBox() - self.city_combo.setFixedWidth(120) # 增加宽度以显示完整城市名 - self.city_combo.addItems([ - '自动定位', - '北京', '上海', '广州', '深圳', '杭州', '南京', '武汉', '成都', '西安', # 一线城市 - '天津', '重庆', '苏州', '青岛', '大连', '宁波', '厦门', '无锡', '佛山', # 新一线城市 - '石家庄', '太原', '呼和浩特', '沈阳', '长春', '哈尔滨', # 东北华北 - '合肥', '福州', '南昌', '济南', '郑州', '长沙', '南宁', '海口', # 华东华中华南 - '贵阳', '昆明', '拉萨', '兰州', '西宁', '银川', '乌鲁木齐' # 西南西北 - ]) - self.city_combo.setCurrentText('自动定位') - self.city_combo.currentTextChanged.connect(self.on_city_changed) - - # 刷新按钮 - self.refresh_weather_btn = QPushButton("刷新天气") - self.refresh_weather_btn.clicked.connect(self.on_refresh_weather) - self.refresh_weather_btn.setFixedSize(80, 25) + # 天气工具组将在需要时动态添加 - weather_layout.addWidget(self.city_combo) - weather_layout.addWidget(self.refresh_weather_btn) - weather_group.setLayout(weather_layout) - ribbon_layout.addWidget(weather_group) - - self.ribbon_area.setLayout(ribbon_layout) + self.ribbon_area.setLayout(self.ribbon_layout) main_layout.addWidget(self.ribbon_area) self.setLayout(main_layout) @@ -186,6 +168,105 @@ class WordRibbon(QFrame): """下划线按钮点击处理""" pass + def create_weather_group(self): + """创建天气组件组""" + if self.weather_group is not None: + return self.weather_group + + weather_group = self.create_ribbon_group("天气") + weather_layout = QVBoxLayout() + + # 城市选择 - 添加所有省会城市 + self.city_combo = QComboBox() + self.city_combo.setFixedWidth(120) # 增加宽度以显示完整城市名 + self.city_combo.addItems([ + '自动定位', + '北京', '上海', '广州', '深圳', '杭州', '南京', '武汉', '成都', '西安', # 一线城市 + '天津', '重庆', '苏州', '青岛', '大连', '宁波', '厦门', '无锡', '佛山', # 新一线城市 + '石家庄', '太原', '呼和浩特', '沈阳', '长春', '哈尔滨', # 东北华北 + '合肥', '福州', '南昌', '济南', '郑州', '长沙', '南宁', '海口', # 华东华中华南 + '贵阳', '昆明', '拉萨', '兰州', '西宁', '银川', '乌鲁木齐' # 西南西北 + ]) + self.city_combo.setCurrentText('自动定位') + self.city_combo.currentTextChanged.connect(self.on_city_changed) + + # 刷新按钮 + self.refresh_weather_btn = QPushButton("刷新天气") + self.refresh_weather_btn.clicked.connect(self.on_refresh_weather) + self.refresh_weather_btn.setFixedSize(80, 25) + + weather_layout.addWidget(self.city_combo) + weather_layout.addWidget(self.refresh_weather_btn) + weather_group.setLayout(weather_layout) + + self.weather_group = weather_group + return weather_group + + def show_weather_group(self): + """显示天气组件""" + if not self.weather_visible and self.weather_group is None: + weather_group = self.create_weather_group() + # 在编辑组之后添加天气组 + if self.ribbon_layout: + # 找到编辑组的位置,在其后插入天气组 + insert_index = self.ribbon_layout.count() - 1 # 在stretch之前插入 + self.ribbon_layout.insertWidget(insert_index, weather_group) + self.weather_visible = True + + def hide_weather_group(self): + """隐藏天气组件""" + if self.weather_visible and self.weather_group is not None: + self.weather_group.setParent(None) + self.weather_group = None + self.weather_visible = False + + def create_quote_group(self): + """创建每日一言组件组""" + if self.quote_group is not None: + return self.quote_group + + quote_group = self.create_ribbon_group("每日一言") + quote_layout = QVBoxLayout() + + # 每日一言显示标签 + self.quote_label = QLabel("每日一言: 暂无") + self.quote_label.setStyleSheet("QLabel { color: #666666; font-style: italic; font-size: 10px; }") + self.quote_label.setWordWrap(True) + self.quote_label.setFixedWidth(150) + + # 刷新按钮 + self.refresh_quote_btn = QPushButton("刷新箴言") + self.refresh_quote_btn.clicked.connect(self.on_refresh_quote) + self.refresh_quote_btn.setFixedSize(80, 25) + + quote_layout.addWidget(self.quote_label) + quote_layout.addWidget(self.refresh_quote_btn) + quote_group.setLayout(quote_layout) + + self.quote_group = quote_group + return quote_group + + def show_quote_group(self): + """显示每日一言组件""" + if not self.quote_visible and self.quote_group is None: + quote_group = self.create_quote_group() + # 在天气组之后添加每日一言组 + if self.ribbon_layout: + # 找到合适的位置插入每日言组 + insert_index = self.ribbon_layout.count() - 1 # 在stretch之前插入 + # 如果天气组件存在,在其后插入;否则在编辑组后插入 + if self.weather_group is not None and self.weather_visible: + insert_index = self.ribbon_layout.indexOf(self.weather_group) + 1 + self.ribbon_layout.insertWidget(insert_index, quote_group) + self.quote_visible = True + + def hide_quote_group(self): + """隐藏每日一言组件""" + if self.quote_visible and self.quote_group is not None: + self.quote_group.setParent(None) + self.quote_group = None + self.quote_visible = False + def create_ribbon_group(self, title): """创建功能区组""" group = QGroupBox(title) @@ -215,6 +296,15 @@ class WordRibbon(QFrame): """城市选择变化处理""" pass + def on_refresh_quote(self): + """刷新每日一言按钮点击处理""" + pass + + def update_quote_display(self, quote_text): + """更新每日一言显示""" + if hasattr(self, 'quote_label') and self.quote_label: + self.quote_label.setText(f"每日一言: {quote_text}") + def create_ribbon_button(self, text, shortcut, icon_name): """创建功能区按钮""" btn = QToolButton() diff --git a/src/word_main_window.py b/src/word_main_window.py index 35646cf..6c247b2 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -340,6 +340,22 @@ class WordStyleMainWindow(QMainWindow): # 天气功能 weather_menu = view_menu.addMenu('天气信息') + # 显示天气工具组 + self.show_weather_tools_action = QAction('显示天气工具', self) + self.show_weather_tools_action.setCheckable(True) + self.show_weather_tools_action.setChecked(False) # 默认不显示 + self.show_weather_tools_action.triggered.connect(self.toggle_weather_tools) + weather_menu.addAction(self.show_weather_tools_action) + + # 显示每日一言工具组 + self.show_quote_tools_action = QAction('显示每日一言工具', self) + self.show_quote_tools_action.setCheckable(True) + self.show_quote_tools_action.setChecked(False) # 默认不显示 + self.show_quote_tools_action.triggered.connect(self.toggle_quote_tools) + weather_menu.addAction(self.show_quote_tools_action) + + weather_menu.addSeparator() + # 刷新天气 refresh_weather_action = QAction('刷新天气', self) refresh_weather_action.setShortcut('F5') @@ -719,6 +735,69 @@ class WordStyleMainWindow(QMainWindow): self.refresh_weather() dialog.close() + def toggle_weather_tools(self, checked): + """切换天气工具组显示""" + if checked: + # 显示天气工具组 + if hasattr(self, 'ribbon'): + self.ribbon.show_weather_group() + self.status_bar.showMessage("天气工具已显示", 2000) + else: + # 隐藏天气工具组 + if hasattr(self, 'ribbon'): + self.ribbon.hide_weather_group() + self.status_bar.showMessage("天气工具已隐藏", 2000) + + def toggle_quote_tools(self, checked): + """切换每日一言工具组显示""" + if checked: + # 显示每日一言工具组 + if hasattr(self, 'ribbon'): + self.ribbon.show_quote_group() + self.status_bar.showMessage("每日一言工具已显示", 2000) + # 如果当前没有显示内容,刷新一次 + if hasattr(self.ribbon, 'quote_label') and self.ribbon.quote_label.text() == "每日一言: 暂无": + self.refresh_daily_quote() + else: + # 隐藏每日一言工具组 + if hasattr(self, 'ribbon'): + self.ribbon.hide_quote_group() + self.status_bar.showMessage("每日一言工具已隐藏", 2000) + + def refresh_daily_quote(self): + """刷新每日一言 - 使用线程获取""" + # 创建并启动获取名言的线程 + self.quote_thread = QuoteFetchThread() + self.quote_thread.quote_fetched.connect(self.on_quote_fetched) + self.quote_thread.start() + + def on_quote_fetched(self, quote_data): + """处理名言获取成功""" + if 'error' not in quote_data: + content = quote_data.get('content', '获取名言失败') + author = quote_data.get('author', '未知') + quote_text = f"{content} — {author}" + + # 更新Ribbon中的每日一言显示 + if hasattr(self.ribbon, 'quote_label'): + self.ribbon.quote_label.setText(f"每日一言: {quote_text}") + + # 更新状态栏 + self.status_bar.showMessage(f"每日名言: {quote_text}", 10000) + else: + # 处理错误情况 + error_msg = quote_data.get('error', '获取名言失败') + if hasattr(self.ribbon, 'quote_label'): + self.ribbon.quote_label.setText(f"每日一言: 获取失败") + self.status_bar.showMessage(f"每日名言获取失败: {error_msg}", 5000) + + def on_quote_error(self, error_data): + """处理名言获取错误""" + error_msg = error_data.get('error', '获取名言失败') if isinstance(error_data, dict) else str(error_data) + if hasattr(self.ribbon, 'quote_label'): + self.ribbon.quote_label.setText(f"每日一言: 获取失败") + self.status_bar.showMessage(f"每日名言获取失败: {error_msg}", 5000) + def update_quote_display(self, quote_data): """更新名言显示""" if 'error' not in quote_data: -- 2.34.1 From 17061452681977e0b73975d3ac7006919074c227 Mon Sep 17 00:00:00 2001 From: Lesacm <1500309685@qq.com> Date: Mon, 20 Oct 2025 10:46:12 +0800 Subject: [PATCH 3/4] 0.2.1 --- WeatherAPI_Integration_Summary.md | 115 ------------------------------ src/ui/word_style_ui.py | 44 +++++++++++- 2 files changed, 43 insertions(+), 116 deletions(-) delete mode 100644 WeatherAPI_Integration_Summary.md diff --git a/WeatherAPI_Integration_Summary.md b/WeatherAPI_Integration_Summary.md deleted file mode 100644 index 9a2b8b9..0000000 --- a/WeatherAPI_Integration_Summary.md +++ /dev/null @@ -1,115 +0,0 @@ -# WeatherAPI 集成总结 - -## 集成状态 -✅ **成功完成** - WeatherAPI类已成功集成到word_main_window.py文件中 - -## 集成功能概览 - -### 1. 核心集成组件 -- **WeatherAPI实例**: 在WordStyleMainWindow类中创建了WeatherAPI实例 -- **WeatherFetchThread线程**: 使用WeatherAPI获取实时天气数据 -- **状态栏显示**: 在状态栏中显示当前天气信息 - -### 2. 新增功能 - -#### 2.1 天气数据获取 -- **自动获取**: WeatherFetchThread线程自动获取北京天气数据 -- **数据格式化**: 将原始天气数据格式化为用户友好的格式 -- **错误处理**: 包含完整的异常处理机制 - -#### 2.2 用户界面功能 -- **状态栏显示**: 在状态栏右侧显示当前温度、天气描述、湿度和风力 -- **菜单集成**: 在"视图"菜单中添加"天气信息"子菜单 -- **快捷键支持**: F5快捷键刷新天气信息 - -#### 2.3 详细天气信息 -- **对话框显示**: 点击"显示详细天气"打开详细天气信息对话框 -- **实时刷新**: 对话框内可手动刷新天气数据 -- **预报信息**: 显示未来3天的天气预报 - -### 3. 技术实现细节 - -#### 3.1 类结构修改 -```python -class WordStyleMainWindow(QMainWindow): - def __init__(self): - # ... 其他初始化代码 - self.weather_api = WeatherAPI() # 新增WeatherAPI实例 - # ... -``` - -#### 3.2 线程实现 -```python -class WeatherFetchThread(QThread): - def __init__(self): - super().__init__() - self.weather_api = WeatherAPI() # 使用WeatherAPI替代NetworkService - - def run(self): - # 使用WeatherAPI获取天气数据 - weather_data = self.weather_api.get_weather_data("北京") - # 格式化并发送信号 -``` - -#### 3.3 菜单集成 -```python -def create_menu_bar(self): - # 在视图菜单中添加天气信息子菜单 - weather_submenu = view_menu.addMenu('天气信息') - - # 刷新天气菜单项 - refresh_weather_action = QAction('刷新天气', self) - refresh_weather_action.setShortcut('F5') - refresh_weather_action.triggered.connect(self.refresh_weather) - - # 显示详细天气菜单项 - show_weather_action = QAction('显示详细天气', self) - show_weather_action.triggered.connect(self.show_detailed_weather) -``` - -## 测试验证 - -### 测试结果 -- ✅ 导入测试: WeatherAPI类导入成功 -- ✅ 方法存在性测试: 所有相关类和方法存在 -- ✅ 功能完整性测试: WeatherAPI功能完整可用 - -### 测试覆盖率 -- WeatherAPI实例创建和初始化 -- 天气数据获取和格式化 -- 用户界面集成 -- 错误处理机制 - -## 使用说明 - -### 基本使用 -1. 启动应用程序 -2. 天气信息自动显示在状态栏右侧 -3. 使用F5快捷键或菜单刷新天气 -4. 点击"显示详细天气"查看详细信息 - -### 功能特点 -- **实时更新**: 天气数据自动更新 -- **用户友好**: 简洁的界面和操作 -- **错误处理**: 网络异常时显示友好提示 -- **扩展性**: 支持未来添加更多城市 - -## 技术优势 - -1. **模块化设计**: WeatherAPI独立封装,便于维护 -2. **线程安全**: 使用QThread避免界面卡顿 -3. **信号机制**: 使用pyqtSignal进行线程间通信 -4. **错误恢复**: 完善的异常处理机制 - -## 未来扩展建议 - -1. **多城市支持**: 添加城市选择功能 -2. **天气预警**: 集成天气预警信息 -3. **主题适配**: 根据天气调整界面主题 -4. **数据缓存**: 添加天气数据缓存机制 - ---- - -**集成完成时间**: 2024年 -**测试状态**: 全部通过 -**代码质量**: 优秀 \ No newline at end of file diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index 39a4b9f..a96f0f9 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -758,4 +758,46 @@ class WeatherAPI: if weather_data: results[city] = weather_data return results - \ No newline at end of file +class daily_sentence_API: + def __init__(self, api_url): + self.api_url = api_url or "https://api.nxvav.cn/api/yiyan" + + def get_sentence(self, format_type='json'): + """ + 获取每日一言数据。 + + 参数: + format_type (str): 指定返回格式,可选值: 'json', 'js', 'js1', 'text'。默认为 'json'。 + + 返回: + 如果 format_type 为 'json',返回解析后的字典(包含 id, yiyan, createTime, nick)。 + 如果 format_type 为 'js', 'js1', 或 'text',返回字符串形式的响应内容。 + 如果请求失败或格式不支持,返回 None。 + """ + # 定义查询参数字典,根据 format_type 添加对应参数 + params = {} + if format_type == 'json': + params['json'] = 'true' + elif format_type == 'js': + params['js'] = 'true' + elif format_type == 'js1': + params['js1'] = 'true' + elif format_type == 'text': + params['text'] = 'true' + else: + print(f"错误:不支持的格式类型 '{format_type}',使用默认 'json'。") + params['json'] = 'true' # 默认为 JSON + + try: + # 发送 GET 请求 + response = requests.get(self.api_url, params=params) + response.raise_for_status() # 如果请求失败(如 4xx 或 5xx),抛出异常 + + # 根据格式类型处理响应 + if format_type == 'json': + return response.json() # 解析为字典 + else: + return response.text # 返回文本内容 + except requests.exceptions.RequestException as e: + print(f"请求失败: {e}") + return None -- 2.34.1 From 7f49a548d760d39e55c45c715212c87bb9cae47e Mon Sep 17 00:00:00 2001 From: Horse861 <929110464@qq.com> Date: Mon, 20 Oct 2025 11:10:49 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=A4=A9=E6=B0=94=E5=92=8C=E6=AF=8F?= =?UTF-8?q?=E6=97=A5=E4=B8=80=E8=A8=80=E4=BF=AE=E5=A4=8D=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main_window.py | 2 +- src/ui/word_style_ui.py | 48 ++++++++++++++++++++++++++++++++++++++--- src/word_main_window.py | 35 +++++++++++++++--------------- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/main_window.py b/src/main_window.py index 49187d4..9522314 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -231,7 +231,7 @@ class MainWindow(QMainWindow): view_menu.addAction(self.quote_action) # 显示天气信息动作 - self.weather_action = QAction('显示天气信息', self) + self.weather_action = QAction('显示天气', self) self.weather_action.setCheckable(True) self.weather_action.setChecked(True) self.weather_action.triggered.connect(self.toggleWeatherDisplay) diff --git a/src/ui/word_style_ui.py b/src/ui/word_style_ui.py index f58a90a..0d90ce2 100644 --- a/src/ui/word_style_ui.py +++ b/src/ui/word_style_ui.py @@ -229,7 +229,7 @@ class WordRibbon(QFrame): quote_layout = QVBoxLayout() # 每日一言显示标签 - self.quote_label = QLabel("每日一言: 暂无") + self.quote_label = QLabel("暂无") self.quote_label.setStyleSheet("QLabel { color: #666666; font-style: italic; font-size: 10px; }") self.quote_label.setWordWrap(True) self.quote_label.setFixedWidth(150) @@ -244,6 +244,10 @@ class WordRibbon(QFrame): quote_group.setLayout(quote_layout) self.quote_group = quote_group + + # 组件创建完成后自动获取每日一言 + self.load_daily_quote() + return quote_group def show_quote_group(self): @@ -296,14 +300,52 @@ class WordRibbon(QFrame): """城市选择变化处理""" pass + def load_daily_quote(self): + """加载每日一言""" + try: + # 创建每日一言API实例 + quote_api = daily_sentence_API("https://api.nxvav.cn/api/yiyan") + + # 获取每日一言数据 + quote_data = quote_api.get_sentence('json') + + if quote_data and isinstance(quote_data, dict): + # 从返回的数据中提取每日一言文本 + quote_text = quote_data.get('yiyan', '暂无每日一言') + self.update_quote_display(quote_text) + else: + # 如果API返回空或格式不正确,显示默认文本 + self.update_quote_display("暂无每日一言") + + except Exception as e: + print(f"加载每日一言失败: {e}") + self.update_quote_display("暂无每日一言") + def on_refresh_quote(self): """刷新每日一言按钮点击处理""" - pass + try: + # 创建每日一言API实例 + quote_api = daily_sentence_API("https://api.nxvav.cn/api/yiyan") + + # 获取每日一言数据 + quote_data = quote_api.get_sentence('json') + + if quote_data and isinstance(quote_data, dict): + # 从返回的数据中提取每日一言文本 + quote_text = quote_data.get('yiyan', '暂无每日一言') + self.update_quote_display(quote_text) + else: + # 如果API返回空或格式不正确,显示默认文本 + self.update_quote_display("获取每日一言失败") + + except Exception as e: + print(f"获取每日一言失败: {e}") + self.update_quote_display("获取每日一言失败") def update_quote_display(self, quote_text): """更新每日一言显示""" if hasattr(self, 'quote_label') and self.quote_label: - self.quote_label.setText(f"每日一言: {quote_text}") + self.quote_label.setText(f"{quote_text}") def create_ribbon_button(self, text, shortcut, icon_name): """创建功能区按钮""" diff --git a/src/word_main_window.py b/src/word_main_window.py index 6c247b2..17642a4 100644 --- a/src/word_main_window.py +++ b/src/word_main_window.py @@ -337,8 +337,8 @@ class WordStyleMainWindow(QMainWindow): view_menu.addSeparator() - # 天气功能 - weather_menu = view_menu.addMenu('天气信息') + # 附加工具功能 + weather_menu = view_menu.addMenu('附加工具') # 显示天气工具组 self.show_weather_tools_action = QAction('显示天气工具', self) @@ -612,7 +612,7 @@ class WordStyleMainWindow(QMainWindow): print(f"接收到天气数据: {weather_data}") if 'error' in weather_data: print(f"天气显示错误: {weather_data['error']}") - self.status_bar.showMessage(f"天气信息获取失败: {weather_data['error']}", 3000) + self.status_bar.showMessage(f"天气数据获取失败: {weather_data['error']}", 3000) else: city = weather_data.get('city', '未知城市') temp = weather_data.get('temperature', 'N/A') @@ -654,9 +654,9 @@ class WordStyleMainWindow(QMainWindow): 'forecast': weather_data['forecast'] } self.update_weather_display(formatted_data) - self.status_bar.showMessage("天气信息已刷新", 2000) + self.status_bar.showMessage("天气数据已刷新", 2000) else: - self.status_bar.showMessage("天气信息刷新失败,请检查API密钥", 3000) + self.status_bar.showMessage("天气数据刷新失败,请检查API密钥", 3000) except Exception as e: self.status_bar.showMessage(f"天气刷新失败: {str(e)}", 3000) @@ -666,14 +666,14 @@ class WordStyleMainWindow(QMainWindow): # 检查是否有天气数据 if not hasattr(self, 'current_weather_data') or not self.current_weather_data: - QMessageBox.information(self, "天气信息", "暂无天气数据,请先刷新天气信息") + QMessageBox.information(self, "附加工具", "暂无天气数据,请先刷新天气信息") return weather_data = self.current_weather_data # 创建对话框 dialog = QDialog(self) - dialog.setWindowTitle("详细天气信息") + dialog.setWindowTitle("详细天气") dialog.setMinimumWidth(400) layout = QVBoxLayout() @@ -765,11 +765,10 @@ class WordStyleMainWindow(QMainWindow): self.status_bar.showMessage("每日一言工具已隐藏", 2000) def refresh_daily_quote(self): - """刷新每日一言 - 使用线程获取""" - # 创建并启动获取名言的线程 - self.quote_thread = QuoteFetchThread() - self.quote_thread.quote_fetched.connect(self.on_quote_fetched) - self.quote_thread.start() + """刷新每日一言 - 使用WordRibbon中的API""" + if hasattr(self, 'ribbon'): + # 直接调用WordRibbon中的刷新方法 + self.ribbon.on_refresh_quote() def on_quote_fetched(self, quote_data): """处理名言获取成功""" @@ -779,23 +778,23 @@ class WordStyleMainWindow(QMainWindow): quote_text = f"{content} — {author}" # 更新Ribbon中的每日一言显示 - if hasattr(self.ribbon, 'quote_label'): - self.ribbon.quote_label.setText(f"每日一言: {quote_text}") + if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'quote_label'): + self.ribbon.quote_label.setText(f"{quote_text}") # 更新状态栏 self.status_bar.showMessage(f"每日名言: {quote_text}", 10000) else: # 处理错误情况 error_msg = quote_data.get('error', '获取名言失败') - if hasattr(self.ribbon, 'quote_label'): - self.ribbon.quote_label.setText(f"每日一言: 获取失败") + if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'quote_label'): + self.ribbon.quote_label.setText(f"获取失败") self.status_bar.showMessage(f"每日名言获取失败: {error_msg}", 5000) def on_quote_error(self, error_data): """处理名言获取错误""" error_msg = error_data.get('error', '获取名言失败') if isinstance(error_data, dict) else str(error_data) - if hasattr(self.ribbon, 'quote_label'): - self.ribbon.quote_label.setText(f"每日一言: 获取失败") + if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'quote_label'): + self.ribbon.quote_label.setText(f"获取失败") self.status_bar.showMessage(f"每日名言获取失败: {error_msg}", 5000) def update_quote_display(self, quote_data): -- 2.34.1