import os
import sys
import json
import time
from dotenv import load_dotenv
from PyQt5.QtWidgets import (
QApplication, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QMessageBox, QScrollArea
)
from PyQt5.QtCore import QTimer, Qt, QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager, QNetworkReply
from PyQt5.QtGui import QPixmap
# 加载 .env 文件
load_dotenv()
REFRESH_TIME = 1000 # 刷新时间,单位为毫秒
class WeatherWidget(QWidget):
def __init__(self):
super().__init__()
self.city = "Dongguan" # 默认城市
self.api_key = os.getenv("OWM_API_KEY") # 从 .env 文件读取 API
if not self.api_key:
QMessageBox.critical(self, "错误", "请设置环境变量 OWM_API_KEY 或在 .env 文件中配置")
sys.exit(1)
self.last_data = {}
self.network_manager = QNetworkAccessManager()
self.network_manager.finished.connect(self.handle_response)
self.init_ui()
self.update_weather()
self.setup_timer()
def init_ui(self):
"""初始化UI界面"""
self.setWindowTitle("天气查询")
self.setMinimumSize(600, 800)
# 设置样式表
self.setStyleSheet("""
QWidget {
background: #f0f0f0;
font-family: Arial, sans-serif;
}
QLabel {
font-size: 16px;
color: #333;
}
QLineEdit, QPushButton {
font-size: 14px;
padding: 8px;
}
QPushButton {
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
}
QPushButton:hover {
background: #45a049;
}
QScrollArea {
border: none;
}
""")
# 主布局
self.layout = QVBoxLayout()
self.setLayout(self.layout)
# 城市输入框和按钮
self.city_input = QLineEdit(self.city)
self.city_input.setPlaceholderText("输入城市名称")
self.search_button = QPushButton("查询")
self.search_button.clicked.connect(self.on_search_clicked)
input_layout = QHBoxLayout()
input_layout.addWidget(self.city_input)
input_layout.addWidget(self.search_button)
self.layout.addLayout(input_layout)
# 天气信息标签
self.labels = {}
self.label_texts = ["日期:", "温度:", "天气:", "湿度:"]
for text in self.label_texts:
self.add_label(text)
# 天气图标
self.weather_icon_label = QLabel()
self.weather_icon_label.setAlignment(Qt.AlignCenter)
self.layout.addWidget(self.weather_icon_label)
# 衣服推荐和注意事项
self.clothing_label = QLabel("衣服推荐: 加载中...")
self.clothing_label.setAlignment(Qt.AlignLeft)
self.layout.addWidget(self.clothing_label)
self.tips_label = QLabel("注意事项: 加载中...")
self.tips_label.setAlignment(Qt.AlignLeft)
self.layout.addWidget(self.tips_label)
# 当天天气预测
self.forecast_label = QLabel("当天天气预测:")
self.forecast_label.setAlignment(Qt.AlignLeft)
self.layout.addWidget(self.forecast_label)
# 滚动区域用于显示当天天气预测
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.scroll_content = QWidget()
self.scroll_layout = QVBoxLayout(self.scroll_content)
self.scroll_area.setWidget(self.scroll_content)
self.layout.addWidget(self.scroll_area)
def add_label(self, text):
"""添加标签到布局中"""
label = QLabel(text)
label.setAlignment(Qt.AlignLeft)
self.layout.addWidget(label)
self.labels[text] = QLabel("加载中...")
self.labels[text].setAlignment(Qt.AlignLeft)
self.layout.addWidget(self.labels[text])
def setup_timer(self):
"""设置定时器,定时更新天气信息"""
self.timer = QTimer()
self.timer.timeout.connect(self.update_weather)
self.timer.start(REFRESH_TIME)
def update_weather(self):
"""发起网络请求,获取天气信息"""
self.labels["日期:"].setText("正在更新...")
api_url = f"http://api.openweathermap.org/data/2.5/weather?q={self.city}&appid={self.api_key}&units=metric"
forecast_url = f"http://api.openweathermap.org/data/2.5/forecast?q={self.city}&appid={self.api_key}&units=metric"
# 请求当前天气
request = QNetworkRequest(QUrl(api_url))
self.network_manager.get(request)
# 请求天气预报
forecast_request = QNetworkRequest(QUrl(forecast_url))
self.network_manager.get(forecast_request)
def handle_response(self, reply):
"""处理网络请求的响应"""
try:
url = reply.url().toString()
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
print(f"请求 URL: {url}, 状态码: {status_code}") # 调试信息
if reply.error() == QNetworkReply.NoError:
data = json.loads(reply.readAll().data())
print("API 返回的数据:", data) # 打印 API 返回的数据
if "weather" in url:
# 处理当前天气数据
self.last_data = {
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"temp": data["main"]["temp"],
"weather": data["weather"][0]["description"],
"humidity": data["main"]["humidity"],
"icon": data["weather"][0]["icon"] # 天气图标代码
}
self.display_data(self.last_data)
self.update_clothing_recommendation(self.last_data["temp"])
self.update_tips(self.last_data["weather"])
elif "forecast" in url:
# 处理天气预报数据
print("预测数据:", data) # 打印预测数据
self.display_forecast(data["list"])
else:
error_message = reply.errorString()
print(f"请求失败: {error_message}")
self.show_error(f"请求失败: {error_message}")
except json.JSONDecodeError:
self.show_error("数据解析失败")
except KeyError as e:
self.show_error(f"数据格式错误: {str(e)}")
except Exception as e:
self.show_error(f"未知错误: {str(e)}")
finally:
reply.deleteLater() # 释放资源
def display_data(self, data):
"""显示天气数据"""
self.labels["日期:"].setText(data.get("date", "N/A"))
self.labels["温度:"].setText(f"{data.get('temp', 'N/A')}°C")
self.labels["天气:"].setText(data.get("weather", "N/A"))
self.labels["湿度:"].setText(f"{data.get('humidity', 'N/A')}%")
# 显示天气图标
icon_code = data.get("icon", "")
if icon_code:
icon_url = f"http://openweathermap.org/img/wn/{icon_code}@2x.png"
self.load_weather_icon(icon_url, self.weather_icon_label)
def load_weather_icon(self, url, icon_label):
"""加载天气图标并显示在指定的标签中"""
request = QNetworkRequest(QUrl(url))
request.setAttribute(QNetworkRequest.User, icon_label) # 将标签对象附加到请求中
self.network_manager.get(request)
def handle_icon_response(self, reply):
"""处理天气图标请求的响应"""
try:
icon_label = reply.request().attribute(QNetworkRequest.User)
if icon_label and reply.error() == QNetworkReply.NoError:
pixmap = QPixmap()
pixmap.loadFromData(reply.readAll())
icon_label.setPixmap(pixmap.scaled(50, 50, Qt.KeepAspectRatio))
except Exception as e:
print(f"加载天气图标失败: {str(e)}")
finally:
reply.deleteLater() # 释放资源
def show_error(self, message):
"""显示错误信息"""
self.labels["日期:"].setText(message)
self.labels["日期:"].setStyleSheet("color: red;")
if self.last_data:
self.display_data(self.last_data)
def on_search_clicked(self):
"""查询按钮时触发"""
city = self.city_input.text().strip()
if city:
self.city = city
self.update_weather()
def update_clothing_recommendation(self, temperature):
"""根据温度更新衣服推荐"""
if temperature < 0:
clothing = "冷爆了,有啥穿啥孩子别冻坏了"
elif temperature < 10:
clothing = "建议穿厚外套、毛衣、围巾和手套。"
elif 10 <= temperature < 20:
clothing = "建议穿外套、长袖衬衫和长裤。"
elif 20 <= temperature < 30:
clothing = "建议穿T恤、短裤或薄长裤。"
else:
clothing = "热死了 建议穿短袖、短裤和凉鞋。"
self.clothing_label.setText(f"衣服推荐: {clothing}")
def update_tips(self, weather_condition):
"""根据天气情况更新注意事项"""
if "rain" in weather_condition.lower():
tips = "今天有雨,记得带伞。"
elif "snow" in weather_condition.lower():
tips = "今天下雪,注意保暖和防滑。"
elif "clear" in weather_condition.lower():
tips = "今天天气晴朗,适合户外活动。"
elif "cloud" in weather_condition.lower():
tips = "今天多云,注意防晒。"
else:
tips = "天气多变,请注意携带雨具和防晒用品。"
self.tips_label.setText(f"注意事项: {tips}")
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = WeatherWidget()
widget.show()
sys.exit(app.exec_())
这是我的程序,遇到了最下面一行需要删除和需要稳定API的问题