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/2] =?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/2] =?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