当前位置: 首页 > news >正文

PYcharm——获取天气

PYcharm——图形化天气组件


在这里插入图片描述

openweather官网
免费的天气API网站,注册订阅即可免费使用

import sys
import requests
import json
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,QHBoxLayout, QLabel, QLineEdit, QPushButton,QTextEdit, QComboBox, QGroupBox, QProgressBar,QMessageBox, QSplitter, QCheckBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFontclass ApiWorker(QThread):"""API请求工作线程"""finished = pyqtSignal(dict)error = pyqtSignal(str)def __init__(self, api_key, city, country_code, lang='zh_cn', original_city=None):super().__init__()self.api_key = api_keyself.city = cityself.country_code = country_codeself.lang = langself.original_city = original_city  # 保存原始中文城市名def run(self):try:base_url = "http://api.openweathermap.org/data/2.5/weather"params = {'q': f'{self.city},{self.country_code}','appid': self.api_key,'units': 'metric','lang': self.lang}response = requests.get(base_url, params=params, timeout=10)result = {'status_code': response.status_code,'data': response.json() if response.status_code == 200 else None,'raw_text': response.text,'request_city': self.city,  # 实际请求的城市名'original_city': self.original_city  # 原始输入的城市名}self.finished.emit(result)except Exception as e:self.error.emit(str(e))class CityTranslator:"""城市名称翻译器"""# 中文到英文的城市名称映射CITY_TRANSLATIONS = {# 中国主要城市'北京': 'Beijing', '上海': 'Shanghai', '广州': 'Guangzhou','深圳': 'Shenzhen', '杭州': 'Hangzhou', '成都': 'Chengdu','重庆': 'Chongqing', '武汉': 'Wuhan', '西安': "Xi'an",'南京': 'Nanjing', '天津': 'Tianjin', '苏州': 'Suzhou','厦门': 'Xiamen', '青岛': 'Qingdao', '大连': 'Dalian','郑州': 'Zhengzhou', '长沙': 'Changsha', '沈阳': 'Shenyang','宁波': 'Ningbo', '无锡': 'Wuxi', '佛山': 'Foshan','东莞': 'Dongguan', '济南': 'Jinan', '合肥': 'Hefei','福州': 'Fuzhou', '昆明': 'Kunming', '哈尔滨': 'Harbin','长春': 'Changchun', '太原': 'Taiyuan', '南宁': 'Nanning','贵阳': 'Guiyang', '兰州': 'Lanzhou', '银川': 'Yinchuan','西宁': 'Xining', '乌鲁木齐': 'Urumqi', '拉萨': 'Lhasa',# 国际城市中文名到英文名'伦敦': 'London', '纽约': 'New York', '东京': 'Tokyo','巴黎': 'Paris', '悉尼': 'Sydney', '首尔': 'Seoul','柏林': 'Berlin', '罗马': 'Rome', '莫斯科': 'Moscow','新加坡': 'Singapore', '曼谷': 'Bangkok', '迪拜': 'Dubai','洛杉矶': 'Los Angeles', '芝加哥': 'Chicago', '多伦多': 'Toronto','墨尔本': 'Melbourne', '大阪': 'Osaka', '京都': 'Kyoto','马德里': 'Madrid', '米兰': 'Milan', '维也纳': 'Vienna','阿姆斯特丹': 'Amsterdam', '布鲁塞尔': 'Brussels', '日内瓦': 'Geneva'}@classmethoddef translate_city(cls, chinese_city):"""将中文城市名翻译成英文"""# 如果输入已经是英文或者是拼音,直接返回if all(ord(c) < 128 for c in chinese_city):return chinese_city, False  # False表示没有进行翻译# 查找翻译translated = cls.CITY_TRANSLATIONS.get(chinese_city)if translated:return translated, True  # True表示进行了翻译# 如果没有找到翻译,返回拼音(简单实现,实际可以使用pinyin库)return chinese_city, Falseclass WeatherApp(QMainWindow):def __init__(self):super().__init__()self.init_ui()def init_ui(self):self.setWindowTitle('天气API测试工具')self.setGeometry(100, 100, 900, 700)# 设置样式self.setStyleSheet("""QMainWindow {background-color: #f0f0f0;}QGroupBox {font-weight: bold;border: 2px solid #cccccc;border-radius: 5px;margin-top: 1ex;padding-top: 10px;}QGroupBox::title {subcontrol-origin: margin;left: 10px;padding: 0 5px 0 5px;}QPushButton {background-color: #4CAF50;border: none;color: white;padding: 8px 16px;text-align: center;text-decoration: none;font-size: 14px;margin: 4px 2px;border-radius: 4px;}QPushButton:hover {background-color: #45a049;}QPushButton:disabled {background-color: #cccccc;}QPushButton#preset {background-color: #2196F3;}QPushButton#preset:hover {background-color: #1976D2;}QTextEdit {border: 1px solid #cccccc;border-radius: 3px;padding: 5px;font-family: Consolas, Monaco, monospace;}QLineEdit {padding: 5px;border: 1px solid #cccccc;border-radius: 3px;}QLabel#translation {color: #666;font-style: italic;background-color: #f8f8f8;padding: 3px;border-radius: 3px;}""")central_widget = QWidget()self.setCentralWidget(central_widget)layout = QVBoxLayout(central_widget)# 创建分割器splitter = QSplitter(Qt.Vertical)layout.addWidget(splitter)# 上半部分:输入区域input_group = QGroupBox("API配置")splitter.addWidget(input_group)self.setup_input_area(input_group)# 下半部分:结果显示区域result_group = QGroupBox("测试结果")splitter.addWidget(result_group)self.setup_result_area(result_group)# 设置分割器比例splitter.setSizes([350, 350])# 状态栏self.statusBar().showMessage('准备就绪')def setup_input_area(self, parent):layout = QVBoxLayout(parent)# API密钥输入api_layout = QHBoxLayout()api_layout.addWidget(QLabel('API密钥:'))self.api_key_input = QLineEdit()self.api_key_input.setPlaceholderText('请输入OpenWeatherMap API密钥')self.api_key_input.setText('请输入OpenWeatherMap API密钥')api_layout.addWidget(self.api_key_input)layout.addLayout(api_layout)# 城市和国家选择location_layout = QHBoxLayout()city_layout = QVBoxLayout()city_layout.addWidget(QLabel('城市名称 (支持中文):'))self.city_input = QLineEdit()self.city_input.setText('北京')self.city_input.setPlaceholderText('输入中文或英文城市名,程序会自动转换')self.city_input.textChanged.connect(self.on_city_changed)city_layout.addWidget(self.city_input)# 城市名翻译显示self.translation_label = QLabel('')self.translation_label.setObjectName('translation')self.translation_label.setVisible(False)city_layout.addWidget(self.translation_label)country_layout = QVBoxLayout()country_layout.addWidget(QLabel('国家代码:'))self.country_combo = QComboBox()countries = [('中国', 'cn'), ('美国', 'us'), ('英国', 'gb'), ('日本', 'jp'),('德国', 'de'), ('法国', 'fr'), ('加拿大', 'ca'), ('澳大利亚', 'au'),('韩国', 'kr'), ('俄罗斯', 'ru'), ('巴西', 'br'), ('印度', 'in')]for country_name, country_code in countries:self.country_combo.addItem(f"{country_name} ({country_code})", country_code)self.country_combo.setCurrentText('中国 (cn)')country_layout.addWidget(self.country_combo)location_layout.addLayout(city_layout)location_layout.addLayout(country_layout)layout.addLayout(location_layout)# 语言设置lang_layout = QHBoxLayout()self.lang_checkbox = QCheckBox('显示中文天气描述')self.lang_checkbox.setChecked(True)lang_layout.addWidget(self.lang_checkbox)lang_layout.addStretch()layout.addLayout(lang_layout)# 预定义城市按钮preset_layout = QVBoxLayout()preset_layout.addWidget(QLabel('快速选择城市:'))# 中国城市按钮chinese_cities_layout = QHBoxLayout()chinese_cities = [('北京', 'cn'), ('上海', 'cn'), ('广州', 'cn'),('深圳', 'cn'), ('杭州', 'cn'), ('成都', 'cn')]for city, country in chinese_cities:btn = QPushButton(city)btn.setObjectName("preset")btn.clicked.connect(lambda checked, c=city, co=country: self.set_preset_city(c, co))chinese_cities_layout.addWidget(btn)preset_layout.addLayout(chinese_cities_layout)# 国际城市按钮intl_cities_layout = QHBoxLayout()intl_cities = [('伦敦', 'gb'), ('纽约', 'us'), ('东京', 'jp'),('巴黎', 'fr'), ('悉尼', 'au'), ('首尔', 'kr')]for city, country in intl_cities:btn = QPushButton(city)btn.setObjectName("preset")btn.clicked.connect(lambda checked, c=city, co=country: self.set_preset_city(c, co))intl_cities_layout.addWidget(btn)preset_layout.addLayout(intl_cities_layout)layout.addLayout(preset_layout)# 测试按钮和进度条button_layout = QHBoxLayout()self.test_btn = QPushButton('测试API连接')self.test_btn.clicked.connect(self.test_api)button_layout.addWidget(self.test_btn)self.clear_btn = QPushButton('清空结果')self.clear_btn.clicked.connect(self.clear_results)button_layout.addWidget(self.clear_btn)layout.addLayout(button_layout)# 进度条self.progress_bar = QProgressBar()self.progress_bar.setVisible(False)layout.addWidget(self.progress_bar)def setup_result_area(self, parent):layout = QVBoxLayout(parent)# 请求信息显示request_layout = QVBoxLayout()request_layout.addWidget(QLabel('请求信息:'))self.request_display = QTextEdit()self.request_display.setMaximumHeight(80)request_layout.addWidget(self.request_display)layout.addLayout(request_layout)# 天气信息显示weather_layout = QVBoxLayout()weather_layout.addWidget(QLabel('天气信息:'))self.weather_display = QTextEdit()self.weather_display.setMaximumHeight(150)weather_layout.addWidget(self.weather_display)layout.addLayout(weather_layout)# 原始JSON显示json_layout = QVBoxLayout()json_layout.addWidget(QLabel('原始API响应:'))self.json_display = QTextEdit()json_layout.addWidget(self.json_display)layout.addLayout(json_layout)def on_city_changed(self):"""城市名输入变化时显示翻译信息"""city = self.city_input.text().strip()if not city:self.translation_label.setVisible(False)returntranslated_city, was_translated = CityTranslator.translate_city(city)if was_translated and city != translated_city:self.translation_label.setText(f'将使用英文名查询: {translated_city}')self.translation_label.setVisible(True)else:self.translation_label.setVisible(False)def set_preset_city(self, city, country):"""设置预定义城市"""self.city_input.setText(city)for i in range(self.country_combo.count()):if self.country_combo.itemData(i) == country:self.country_combo.setCurrentIndex(i)breakdef get_country_code(self):"""获取选中的国家代码"""return self.country_combo.currentData()def get_language_code(self):"""获取语言代码"""return 'zh_cn' if self.lang_checkbox.isChecked() else 'en'def test_api(self):"""测试API连接"""api_key = self.api_key_input.text().strip()original_city = self.city_input.text().strip()country_code = self.get_country_code()lang = self.get_language_code()if not api_key:QMessageBox.warning(self, '输入错误', '请输入API密钥')returnif not original_city:QMessageBox.warning(self, '输入错误', '请输入城市名称')return# 翻译城市名request_city, was_translated = CityTranslator.translate_city(original_city)# 更新UI状态self.test_btn.setEnabled(False)self.progress_bar.setVisible(True)self.progress_bar.setRange(0, 0)# 显示请求信息request_info = f"输入城市: {original_city}"if was_translated and original_city != request_city:request_info += f"\n转换城市: {request_city}"request_info += f"\n国家代码: {country_code}"self.request_display.setText(request_info)self.statusBar().showMessage(f'正在查询 {request_city} 的天气...')# 在工作线程中执行API请求self.worker = ApiWorker(api_key, request_city, country_code, lang, original_city)self.worker.finished.connect(self.on_api_finished)self.worker.error.connect(self.on_api_error)self.worker.start()def on_api_finished(self, result):"""API请求完成"""self.test_btn.setEnabled(True)self.progress_bar.setVisible(False)status_code = result['status_code']request_city = result['request_city']original_city = result['original_city']# 更新请求信息current_request_info = self.request_display.toPlainText()current_request_info += f"\nAPI请求: {request_city},{result.get('country_code', '')}"current_request_info += f"\nHTTP状态: {status_code}"self.request_display.setText(current_request_info)if status_code == 200:data = result['data']self.display_weather_info(data, original_city, request_city)self.display_json_response(data)api_city_name = data.get('name', '未知城市')self.statusBar().showMessage(f'成功获取 {api_city_name} 的天气信息')elif status_code == 401:self.show_error('API密钥无效', '请检查API密钥是否正确且已激活')self.statusBar().showMessage('API密钥无效')elif status_code == 404:self.show_city_not_found_error(original_city, request_city)self.statusBar().showMessage('城市未找到')elif status_code == 429:self.show_error('请求频率超限', '请稍后再试或检查API调用限制')self.statusBar().showMessage('请求频率超限')else:error_msg = f"HTTP错误代码: {status_code}\n响应: {result['raw_text']}"self.show_error('请求失败', error_msg)self.statusBar().showMessage(f'请求失败: {status_code}')def on_api_error(self, error_msg):"""API请求出错"""self.test_btn.setEnabled(True)self.progress_bar.setVisible(False)self.show_error('请求异常', error_msg)self.statusBar().showMessage('请求异常')def display_weather_info(self, data, original_city, request_city):"""显示天气信息"""try:api_city = data.get('name', 'N/A')country = data.get('sys', {}).get('country', 'N/A')temp = data.get('main', {}).get('temp', 'N/A')feels_like = data.get('main', {}).get('feels_like', 'N/A')humidity = data.get('main', {}).get('humidity', 'N/A')pressure = data.get('main', {}).get('pressure', 'N/A')description = data.get('weather', [{}])[0].get('description', 'N/A')wind_speed = data.get('wind', {}).get('speed', 'N/A')info_text = f"""
🏙️  位置: {api_city}, {country}
💬  输入: {original_city} → 请求: {request_city}
🌡️  温度: {temp}°C (体感: {feels_like}°C)
🌤️  天气: {description}
💧  湿度: {humidity}%
📈  气压: {pressure} hPa
💨  风速: {wind_speed} m/s""".strip()self.weather_display.setText(info_text)except Exception as e:self.weather_display.setText(f"解析天气数据时出错: {str(e)}")def show_city_not_found_error(self, original_city, request_city):"""显示城市未找到的错误信息"""error_msg = f"""
找不到城市: "{original_city}"查询详情:
• 输入城市: {original_city}
• 转换城市: {request_city}
• 国家代码: {self.get_country_code()}建议:
1. 检查城市名拼写
2. 尝试使用英文名称
3. 检查国家代码是否正确
4. 该城市可能不在API数据库中""".strip()self.show_error('城市未找到', error_msg)def display_json_response(self, data):"""显示原始JSON响应"""try:formatted_json = json.dumps(data, indent=2, ensure_ascii=False)self.json_display.setText(formatted_json)except Exception as e:self.json_display.setText(f"格式化JSON时出错: {str(e)}")def show_error(self, title, message):"""显示错误信息"""self.weather_display.setText(f"❌ {title}\n{message}")self.json_display.setText("")def clear_results(self):"""清空结果显示"""self.request_display.clear()self.weather_display.clear()self.json_display.clear()self.translation_label.setVisible(False)self.statusBar().showMessage('结果已清空')def main():app = QApplication(sys.argv)# 设置应用程序字体font = QFont("Microsoft YaHei", 10)app.setFont(font)window = WeatherApp()window.show()sys.exit(app.exec_())if __name__ == '__main__':main()
http://www.dtcms.com/a/494386.html

相关文章:

  • Kafka多网卡环境配置
  • TypeScript 与淘宝 API:构建类型安全的商品数据查询前端 / Node.js 服务
  • 网站备案名称要求郴州网站排名优化
  • 百度做一个网站多少钱sns营销
  • List<map<String,Object>下划线转驼峰
  • List.subList() 返回值为什么不能强转成 ArrayList
  • phpcms网站转移网站关键词百度排名在下降
  • mac使用本地jdk启动elasticsearch解决elasticsearch启动时jdk损坏问题
  • 手机在初次联网的底层流程-关于EPC信令附着
  • 2025年红米手机上市了哪些款式,本别包含哪些版本,就上市时间、硬件参数、性能、价格等方面进行对比,加入横向竞品对比分析,按价位段划分推荐人群。
  • Go Web 编程快速入门 02 - 认识 net/http 与 Handler 接口
  • 成都网站建设网站制作济南网站制作哪家强
  • 广州做网站的网络公司网站建设美文
  • 云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
  • 虚拟机监控全攻略:从基础到云原生实战
  • fastgpt 社区版探究:mongo db 全文检索算法探秘
  • 防爆手机与普通手机有什么区别?防爆手机哪个牌子好?
  • 聊聊 Unity(小白专享、C# 小程序 之 日历、小闹钟)
  • 在vscode中全选后,同时在每行行尾,开始多行编辑(Mac版)
  • C4D域的重要修改层之延迟衰减和量化之解析
  • 建设银行网站网址是什么柳州电商网站建设
  • 记录WinFrom 使用 Autoupdater.NET.Official 进行软件升级更新,避免遗忘
  • 【汇编】RAX,eax,ax,ah,al 关系
  • 苍穹外卖 Day12 实战总结:Apache POI 实现 Excel 报表导出全流程解析
  • 网站分页符怎么做珠海网站建设哪个好薇
  • Redis的Docker安装
  • Windows 11 24H2 图形化安装 Docker Desktop(自定义安装路径到 D 盘)
  • python+uniapp基于微信小程序的瑜伽体验课预约系统
  • 什么是Bug呢?
  • 怎么制作网站记事本嘉兴网络科技有限公司