|
|
|
|
@ -0,0 +1,609 @@
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
天气悬浮窗口模块
|
|
|
|
|
提供一个可拖拽的天气悬浮窗口,显示当前天气信息
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
from PyQt5.QtWidgets import (
|
|
|
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
|
|
|
|
QFrame, QTextEdit, QDialog, QComboBox
|
|
|
|
|
)
|
|
|
|
|
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QPoint, QThread
|
|
|
|
|
from PyQt5.QtGui import QFont, QPalette, QColor
|
|
|
|
|
|
|
|
|
|
# 导入主题管理器
|
|
|
|
|
from .theme_manager import theme_manager
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WeatherFloatingWidget(QDialog):
|
|
|
|
|
"""天气悬浮窗口类"""
|
|
|
|
|
|
|
|
|
|
# 自定义信号
|
|
|
|
|
closed = pyqtSignal()
|
|
|
|
|
refresh_requested = pyqtSignal()
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.drag_position = None
|
|
|
|
|
self.is_dragging = False
|
|
|
|
|
self.weather_data = None
|
|
|
|
|
self.setup_ui()
|
|
|
|
|
self.setup_connections()
|
|
|
|
|
self.setup_theme()
|
|
|
|
|
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
|
|
|
|
|
self.setAttribute(Qt.WA_TranslucentBackground)
|
|
|
|
|
|
|
|
|
|
def setup_ui(self):
|
|
|
|
|
"""设置UI界面"""
|
|
|
|
|
# 设置窗口属性
|
|
|
|
|
self.setWindowTitle("天气")
|
|
|
|
|
self.setFixedSize(400, 320) # 增加窗口尺寸
|
|
|
|
|
|
|
|
|
|
# 创建主框架,用于实现圆角和阴影效果
|
|
|
|
|
self.main_frame = QFrame()
|
|
|
|
|
self.main_frame.setObjectName("mainFrame")
|
|
|
|
|
|
|
|
|
|
# 主布局
|
|
|
|
|
main_layout = QVBoxLayout(self)
|
|
|
|
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
main_layout.addWidget(self.main_frame)
|
|
|
|
|
|
|
|
|
|
# 内容布局
|
|
|
|
|
content_layout = QVBoxLayout(self.main_frame)
|
|
|
|
|
content_layout.setContentsMargins(15, 15, 15, 15) # 减小内边距
|
|
|
|
|
content_layout.setSpacing(10) # 减小间距
|
|
|
|
|
|
|
|
|
|
# 设置最小尺寸策略
|
|
|
|
|
self.main_frame.setMinimumSize(380, 300)
|
|
|
|
|
|
|
|
|
|
# 标题栏
|
|
|
|
|
title_layout = QHBoxLayout()
|
|
|
|
|
|
|
|
|
|
self.title_label = QLabel("天气信息")
|
|
|
|
|
self.title_label.setFont(QFont("Arial", 12, QFont.Bold))
|
|
|
|
|
title_layout.addWidget(self.title_label)
|
|
|
|
|
title_layout.addStretch()
|
|
|
|
|
|
|
|
|
|
# 关闭按钮
|
|
|
|
|
self.close_btn = QPushButton("×")
|
|
|
|
|
self.close_btn.setFixedSize(20, 20)
|
|
|
|
|
self.close_btn.setObjectName("closeButton")
|
|
|
|
|
title_layout.addWidget(self.close_btn)
|
|
|
|
|
|
|
|
|
|
content_layout.addLayout(title_layout)
|
|
|
|
|
|
|
|
|
|
# 分隔线
|
|
|
|
|
separator = QFrame()
|
|
|
|
|
separator.setFrameShape(QFrame.HLine)
|
|
|
|
|
separator.setObjectName("separator")
|
|
|
|
|
content_layout.addWidget(separator)
|
|
|
|
|
|
|
|
|
|
# 天气图标和温度显示区域
|
|
|
|
|
weather_display_layout = QHBoxLayout()
|
|
|
|
|
weather_display_layout.setSpacing(8) # 适当间距
|
|
|
|
|
weather_display_layout.setContentsMargins(2, 2, 2, 2) # 减小内边距
|
|
|
|
|
|
|
|
|
|
self.weather_icon_label = QLabel("🌞")
|
|
|
|
|
self.weather_icon_label.setFont(QFont("Arial", 28))
|
|
|
|
|
self.weather_icon_label.setAlignment(Qt.AlignCenter)
|
|
|
|
|
self.weather_icon_label.setFixedSize(60, 60)
|
|
|
|
|
weather_display_layout.addWidget(self.weather_icon_label)
|
|
|
|
|
|
|
|
|
|
# 温度和城市信息
|
|
|
|
|
temp_city_layout = QVBoxLayout()
|
|
|
|
|
temp_city_layout.setSpacing(8) # 增加间距
|
|
|
|
|
temp_city_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
|
self.temperature_label = QLabel("25°C")
|
|
|
|
|
self.temperature_label.setFont(QFont("Arial", 20, QFont.Bold))
|
|
|
|
|
self.temperature_label.setObjectName("temperatureLabel")
|
|
|
|
|
temp_city_layout.addWidget(self.temperature_label)
|
|
|
|
|
|
|
|
|
|
self.city_label = QLabel("北京")
|
|
|
|
|
self.city_label.setFont(QFont("Arial", 12))
|
|
|
|
|
self.city_label.setObjectName("cityLabel")
|
|
|
|
|
temp_city_layout.addWidget(self.city_label)
|
|
|
|
|
|
|
|
|
|
weather_display_layout.addLayout(temp_city_layout)
|
|
|
|
|
weather_display_layout.addStretch()
|
|
|
|
|
|
|
|
|
|
content_layout.addLayout(weather_display_layout)
|
|
|
|
|
|
|
|
|
|
# 天气描述
|
|
|
|
|
self.weather_desc_label = QLabel("晴天")
|
|
|
|
|
self.weather_desc_label.setFont(QFont("Arial", 12))
|
|
|
|
|
self.weather_desc_label.setObjectName("weatherDescLabel")
|
|
|
|
|
self.weather_desc_label.setAlignment(Qt.AlignCenter)
|
|
|
|
|
content_layout.addWidget(self.weather_desc_label)
|
|
|
|
|
|
|
|
|
|
# 详细信息(湿度、风速)
|
|
|
|
|
details_layout = QHBoxLayout()
|
|
|
|
|
details_layout.setSpacing(10) # 适当间距
|
|
|
|
|
details_layout.setContentsMargins(2, 2, 2, 2) # 减小内边距
|
|
|
|
|
|
|
|
|
|
self.humidity_label = QLabel("湿度: 45%")
|
|
|
|
|
self.humidity_label.setFont(QFont("Arial", 11))
|
|
|
|
|
self.humidity_label.setObjectName("detailLabel")
|
|
|
|
|
details_layout.addWidget(self.humidity_label)
|
|
|
|
|
|
|
|
|
|
self.wind_label = QLabel("风速: 2级")
|
|
|
|
|
self.wind_label.setFont(QFont("Arial", 11))
|
|
|
|
|
self.wind_label.setObjectName("detailLabel")
|
|
|
|
|
details_layout.addWidget(self.wind_label)
|
|
|
|
|
|
|
|
|
|
content_layout.addLayout(details_layout)
|
|
|
|
|
|
|
|
|
|
# 城市选择区域
|
|
|
|
|
city_layout = QHBoxLayout()
|
|
|
|
|
city_layout.setSpacing(10)
|
|
|
|
|
city_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
|
self.city_combo = QComboBox()
|
|
|
|
|
self.city_combo.setObjectName("cityCombo")
|
|
|
|
|
# 添加所有省会城市,与主窗口保持一致
|
|
|
|
|
self.city_combo.addItems([
|
|
|
|
|
'自动定位',
|
|
|
|
|
'北京', '上海', '广州', '深圳', '杭州', '南京', '武汉', '成都', '西安', # 一线城市
|
|
|
|
|
'天津', '重庆', '苏州', '青岛', '大连', '宁波', '厦门', '无锡', '佛山', # 新一线城市
|
|
|
|
|
'石家庄', '太原', '呼和浩特', '沈阳', '长春', '哈尔滨', # 东北华北
|
|
|
|
|
'合肥', '福州', '南昌', '济南', '郑州', '长沙', '南宁', '海口', # 华东华中华南
|
|
|
|
|
'贵阳', '昆明', '拉萨', '兰州', '西宁', '银川', '乌鲁木齐' # 西南西北
|
|
|
|
|
])
|
|
|
|
|
self.city_combo.setFixedWidth(120) # 增加城市选择框宽度,与主窗口保持一致
|
|
|
|
|
city_layout.addWidget(self.city_combo)
|
|
|
|
|
|
|
|
|
|
city_layout.addStretch()
|
|
|
|
|
|
|
|
|
|
content_layout.addLayout(city_layout)
|
|
|
|
|
|
|
|
|
|
# 按钮区域
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
button_layout.setSpacing(10)
|
|
|
|
|
button_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
|
self.refresh_btn = QPushButton("刷新")
|
|
|
|
|
self.refresh_btn.setObjectName("refreshButton")
|
|
|
|
|
button_layout.addWidget(self.refresh_btn)
|
|
|
|
|
|
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
|
|
|
|
|
self.detail_btn = QPushButton("详情")
|
|
|
|
|
self.detail_btn.setObjectName("detailButton")
|
|
|
|
|
button_layout.addWidget(self.detail_btn)
|
|
|
|
|
|
|
|
|
|
content_layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
# 添加弹性空间
|
|
|
|
|
content_layout.addStretch()
|
|
|
|
|
|
|
|
|
|
def setup_connections(self):
|
|
|
|
|
"""设置信号连接"""
|
|
|
|
|
self.close_btn.clicked.connect(self.close_window)
|
|
|
|
|
self.refresh_btn.clicked.connect(self.on_refresh_clicked)
|
|
|
|
|
self.detail_btn.clicked.connect(self.show_detailed_weather)
|
|
|
|
|
self.city_combo.currentTextChanged.connect(self.on_city_changed)
|
|
|
|
|
|
|
|
|
|
def setup_theme(self):
|
|
|
|
|
"""设置主题"""
|
|
|
|
|
# 连接主题切换信号
|
|
|
|
|
theme_manager.theme_changed.connect(self.on_theme_changed)
|
|
|
|
|
|
|
|
|
|
# 应用当前主题
|
|
|
|
|
self.apply_theme()
|
|
|
|
|
|
|
|
|
|
def apply_theme(self):
|
|
|
|
|
"""应用主题样式"""
|
|
|
|
|
is_dark = theme_manager.is_dark_theme()
|
|
|
|
|
colors = theme_manager.get_current_theme_colors()
|
|
|
|
|
|
|
|
|
|
if is_dark:
|
|
|
|
|
# 深色主题样式
|
|
|
|
|
self.main_frame.setStyleSheet(f"""
|
|
|
|
|
QFrame#mainFrame {{
|
|
|
|
|
background-color: {colors['surface']};
|
|
|
|
|
border: 1px solid {colors['border']};
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel {{
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#temperatureLabel {{
|
|
|
|
|
color: {colors['accent']};
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
margin: 3px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#cityLabel {{
|
|
|
|
|
color: {colors['text_secondary']};
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
padding: 3px 5px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#weatherDescLabel {{
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#detailLabel {{
|
|
|
|
|
color: {colors['text_secondary']};
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
padding: 3px 5px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QFrame#separator {{
|
|
|
|
|
background-color: {colors['border']};
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#closeButton {{
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#closeButton:hover {{
|
|
|
|
|
background-color: #e81123;
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#refreshButton, QPushButton#detailButton {{
|
|
|
|
|
background-color: {colors['accent']};
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 6px 16px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#refreshButton:hover, QPushButton#detailButton:hover {{
|
|
|
|
|
background-color: {colors['accent_hover']};
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo {{
|
|
|
|
|
background-color: {colors['surface']};
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
border: 1px solid {colors['border']};
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
min-height: 24px;
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo:hover {{
|
|
|
|
|
border-color: {colors['accent']};
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo::drop-down {{
|
|
|
|
|
border: none;
|
|
|
|
|
width: 15px;
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo::down-arrow {{
|
|
|
|
|
image: none;
|
|
|
|
|
border-left: 3px solid transparent;
|
|
|
|
|
border-right: 3px solid transparent;
|
|
|
|
|
border-top: 5px solid {colors['text']};
|
|
|
|
|
}}
|
|
|
|
|
""")
|
|
|
|
|
else:
|
|
|
|
|
# 浅色主题样式
|
|
|
|
|
self.main_frame.setStyleSheet(f"""
|
|
|
|
|
QFrame#mainFrame {{
|
|
|
|
|
background-color: {colors['surface']};
|
|
|
|
|
border: 1px solid {colors['border']};
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}}
|
|
|
|
|
QLabel {{
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#temperatureLabel {{
|
|
|
|
|
color: {colors['accent']};
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
margin: 3px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#cityLabel {{
|
|
|
|
|
color: {colors['text_secondary']};
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
padding: 3px 5px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#weatherDescLabel {{
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QLabel#detailLabel {{
|
|
|
|
|
color: {colors['text_secondary']};
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
padding: 3px 5px;
|
|
|
|
|
margin: 2px;
|
|
|
|
|
}}
|
|
|
|
|
QFrame#separator {{
|
|
|
|
|
background-color: {colors['border']};
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#closeButton {{
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#closeButton:hover {{
|
|
|
|
|
background-color: #e81123;
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#refreshButton, QPushButton#detailButton {{
|
|
|
|
|
background-color: {colors['accent']};
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 6px 16px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}}
|
|
|
|
|
QPushButton#refreshButton:hover, QPushButton#detailButton:hover {{
|
|
|
|
|
background-color: {colors['accent_hover']};
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo {{
|
|
|
|
|
background-color: {colors['surface']};
|
|
|
|
|
color: {colors['text']};
|
|
|
|
|
border: 1px solid {colors['border']};
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
min-height: 24px;
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo:hover {{
|
|
|
|
|
border-color: {colors['accent']};
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo::drop-down {{
|
|
|
|
|
border: none;
|
|
|
|
|
width: 15px;
|
|
|
|
|
}}
|
|
|
|
|
QComboBox#cityCombo::down-arrow {{
|
|
|
|
|
image: none;
|
|
|
|
|
border-left: 3px solid transparent;
|
|
|
|
|
border-right: 3px solid transparent;
|
|
|
|
|
border-top: 5px solid {colors['text']};
|
|
|
|
|
}}
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
def on_theme_changed(self, is_dark):
|
|
|
|
|
"""主题切换槽函数"""
|
|
|
|
|
self.apply_theme()
|
|
|
|
|
|
|
|
|
|
def mousePressEvent(self, event):
|
|
|
|
|
"""鼠标按下事件,用于拖拽"""
|
|
|
|
|
if event.button() == Qt.LeftButton:
|
|
|
|
|
# 检查是否点击在标题栏区域
|
|
|
|
|
if event.pos().y() <= 40: # 假设标题栏高度为40像素
|
|
|
|
|
self.is_dragging = True
|
|
|
|
|
self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
|
|
|
|
|
event.accept()
|
|
|
|
|
|
|
|
|
|
def mouseMoveEvent(self, event):
|
|
|
|
|
"""鼠标移动事件,用于拖拽"""
|
|
|
|
|
if self.is_dragging and event.buttons() == Qt.LeftButton:
|
|
|
|
|
self.move(event.globalPos() - self.drag_position)
|
|
|
|
|
event.accept()
|
|
|
|
|
|
|
|
|
|
def mouseReleaseEvent(self, event):
|
|
|
|
|
"""鼠标释放事件"""
|
|
|
|
|
self.is_dragging = False
|
|
|
|
|
|
|
|
|
|
def update_weather(self, weather_data):
|
|
|
|
|
"""更新天气信息"""
|
|
|
|
|
self.weather_data = weather_data
|
|
|
|
|
|
|
|
|
|
if weather_data and 'error' not in weather_data:
|
|
|
|
|
# 获取天气数据
|
|
|
|
|
city = weather_data.get('city', '未知城市')
|
|
|
|
|
current_data = weather_data.get('current', {})
|
|
|
|
|
temp = current_data.get('temp', 'N/A')
|
|
|
|
|
desc = current_data.get('weather', 'N/A')
|
|
|
|
|
humidity = current_data.get('humidity', 'N/A')
|
|
|
|
|
wind_scale = current_data.get('wind_scale', 'N/A')
|
|
|
|
|
|
|
|
|
|
# 更新显示
|
|
|
|
|
self.city_label.setText(city)
|
|
|
|
|
self.temperature_label.setText(f"{temp}°C")
|
|
|
|
|
self.weather_desc_label.setText(desc)
|
|
|
|
|
self.humidity_label.setText(f"湿度: {humidity}%")
|
|
|
|
|
self.wind_label.setText(f"风速: {wind_scale}级")
|
|
|
|
|
|
|
|
|
|
# 更新天气图标
|
|
|
|
|
emoji = self.get_weather_emoji(desc)
|
|
|
|
|
self.weather_icon_label.setText(emoji)
|
|
|
|
|
else:
|
|
|
|
|
# 显示错误信息
|
|
|
|
|
self.city_label.setText("获取失败")
|
|
|
|
|
self.temperature_label.setText("--°C")
|
|
|
|
|
self.weather_desc_label.setText("无法获取天气数据")
|
|
|
|
|
self.humidity_label.setText("湿度: --%")
|
|
|
|
|
self.wind_label.setText("风速: --级")
|
|
|
|
|
self.weather_icon_label.setText("❓")
|
|
|
|
|
|
|
|
|
|
def get_weather_emoji(self, weather_desc):
|
|
|
|
|
"""根据天气描述返回对应的emoji"""
|
|
|
|
|
if not weather_desc:
|
|
|
|
|
return "🌞"
|
|
|
|
|
|
|
|
|
|
weather_desc_lower = weather_desc.lower()
|
|
|
|
|
|
|
|
|
|
# 天气图标映射
|
|
|
|
|
weather_emoji_map = {
|
|
|
|
|
'晴': '🌞',
|
|
|
|
|
'多云': '⛅',
|
|
|
|
|
'阴': '☁️',
|
|
|
|
|
'雨': '🌧️',
|
|
|
|
|
'小雨': '🌦️',
|
|
|
|
|
'中雨': '🌧️',
|
|
|
|
|
'大雨': '⛈️',
|
|
|
|
|
'暴雨': '🌩️',
|
|
|
|
|
'雪': '❄️',
|
|
|
|
|
'小雪': '🌨️',
|
|
|
|
|
'中雪': '❄️',
|
|
|
|
|
'大雪': '☃️',
|
|
|
|
|
'雾': '🌫️',
|
|
|
|
|
'霾': '😷',
|
|
|
|
|
'风': '💨',
|
|
|
|
|
'大风': '🌪️',
|
|
|
|
|
'雷': '⛈️',
|
|
|
|
|
'雷阵雨': '⛈️',
|
|
|
|
|
'冰雹': '🌨️',
|
|
|
|
|
'沙尘': '🌪️'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for key, emoji in weather_emoji_map.items():
|
|
|
|
|
if key in weather_desc_lower:
|
|
|
|
|
return emoji
|
|
|
|
|
|
|
|
|
|
# 默认返回晴天图标
|
|
|
|
|
return '🌞'
|
|
|
|
|
|
|
|
|
|
def on_refresh_clicked(self):
|
|
|
|
|
"""刷新按钮点击事件"""
|
|
|
|
|
self.refresh_requested.emit()
|
|
|
|
|
|
|
|
|
|
def on_city_changed(self, city_name):
|
|
|
|
|
"""城市选择变化事件"""
|
|
|
|
|
# 发射城市变化信号,通知主窗口更新天气
|
|
|
|
|
if hasattr(self.parent(), 'on_city_changed'):
|
|
|
|
|
self.parent().on_city_changed(city_name)
|
|
|
|
|
|
|
|
|
|
def set_current_city(self, city_name):
|
|
|
|
|
"""设置当前城市"""
|
|
|
|
|
# 阻止信号发射,避免循环调用
|
|
|
|
|
self.city_combo.blockSignals(True)
|
|
|
|
|
index = self.city_combo.findText(city_name)
|
|
|
|
|
if index >= 0:
|
|
|
|
|
self.city_combo.setCurrentIndex(index)
|
|
|
|
|
self.city_combo.blockSignals(False)
|
|
|
|
|
|
|
|
|
|
def close_window(self):
|
|
|
|
|
"""关闭窗口 - 只是隐藏而不是销毁"""
|
|
|
|
|
try:
|
|
|
|
|
self.closed.emit()
|
|
|
|
|
self.hide() # 隐藏窗口而不是销毁
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error in close_window: {e}")
|
|
|
|
|
|
|
|
|
|
def show_detailed_weather(self):
|
|
|
|
|
"""显示详细天气信息对话框"""
|
|
|
|
|
# 检查是否有天气数据
|
|
|
|
|
if not self.weather_data or 'error' in self.weather_data:
|
|
|
|
|
from PyQt5.QtWidgets import QMessageBox
|
|
|
|
|
QMessageBox.information(self, "天气信息", "暂无天气数据,请先刷新天气信息")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit
|
|
|
|
|
|
|
|
|
|
weather_data = self.weather_data
|
|
|
|
|
|
|
|
|
|
# 创建对话框
|
|
|
|
|
dialog = QDialog(self)
|
|
|
|
|
dialog.setWindowTitle("详细天气")
|
|
|
|
|
dialog.setMinimumWidth(400)
|
|
|
|
|
|
|
|
|
|
layout = QVBoxLayout()
|
|
|
|
|
|
|
|
|
|
# 城市信息
|
|
|
|
|
city_label = QLabel(f"<h2>{weather_data.get('city', '未知城市')}</h2>")
|
|
|
|
|
layout.addWidget(city_label)
|
|
|
|
|
|
|
|
|
|
# 当前天气信息
|
|
|
|
|
current_layout = QVBoxLayout()
|
|
|
|
|
current_layout.addWidget(QLabel("<b>当前天气:</b>"))
|
|
|
|
|
|
|
|
|
|
# 获取温度信息,支持嵌套结构
|
|
|
|
|
current_data = weather_data.get('current', {})
|
|
|
|
|
temp = current_data.get('temp', 'N/A')
|
|
|
|
|
if temp != 'N/A' and isinstance(temp, str):
|
|
|
|
|
temp = float(temp) if temp.replace('.', '').isdigit() else temp
|
|
|
|
|
|
|
|
|
|
# 从预报数据中获取最高和最低气温
|
|
|
|
|
temp_range = ""
|
|
|
|
|
temp_max = 'N/A'
|
|
|
|
|
temp_min = 'N/A'
|
|
|
|
|
if 'forecast' in weather_data and weather_data['forecast']:
|
|
|
|
|
forecast_data = weather_data['forecast'][0] # 今天的预报
|
|
|
|
|
if isinstance(forecast_data, dict):
|
|
|
|
|
temp_max = forecast_data.get('temp_max', 'N/A')
|
|
|
|
|
temp_min = forecast_data.get('temp_min', 'N/A')
|
|
|
|
|
if temp_max != 'N/A' and temp_min != 'N/A':
|
|
|
|
|
temp_range = f" ({temp_min}°C~{temp_max}°C)"
|
|
|
|
|
|
|
|
|
|
current_info = f"""
|
|
|
|
|
当前温度: {temp}°C{temp_range}
|
|
|
|
|
最高气温: {temp_max}°C
|
|
|
|
|
最低气温: {temp_min}°C
|
|
|
|
|
天气状况: {current_data.get('weather', 'N/A')}
|
|
|
|
|
"""
|
|
|
|
|
current_text = QTextEdit()
|
|
|
|
|
current_text.setPlainText(current_info.strip())
|
|
|
|
|
current_text.setReadOnly(True)
|
|
|
|
|
current_layout.addWidget(current_text)
|
|
|
|
|
|
|
|
|
|
layout.addLayout(current_layout)
|
|
|
|
|
|
|
|
|
|
# 生活提示信息(替换原来的天气预报)
|
|
|
|
|
life_tips = weather_data.get('life_tips', [])
|
|
|
|
|
if life_tips:
|
|
|
|
|
tips_layout = QVBoxLayout()
|
|
|
|
|
tips_layout.addWidget(QLabel("<b>生活提示:</b>"))
|
|
|
|
|
|
|
|
|
|
tips_text = QTextEdit()
|
|
|
|
|
tips_info = ""
|
|
|
|
|
for tip in life_tips:
|
|
|
|
|
tips_info += f"• {tip}\n"
|
|
|
|
|
|
|
|
|
|
tips_text.setPlainText(tips_info.strip())
|
|
|
|
|
tips_text.setReadOnly(True)
|
|
|
|
|
tips_layout.addWidget(tips_text)
|
|
|
|
|
layout.addLayout(tips_layout)
|
|
|
|
|
|
|
|
|
|
# 按钮
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
refresh_button = QPushButton("刷新")
|
|
|
|
|
refresh_button.clicked.connect(lambda: self.refresh_weather_and_close(dialog))
|
|
|
|
|
close_button = QPushButton("关闭")
|
|
|
|
|
close_button.clicked.connect(dialog.close)
|
|
|
|
|
|
|
|
|
|
button_layout.addWidget(refresh_button)
|
|
|
|
|
button_layout.addWidget(close_button)
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
dialog.setLayout(layout)
|
|
|
|
|
dialog.exec_()
|
|
|
|
|
|
|
|
|
|
def refresh_weather_and_close(self, dialog):
|
|
|
|
|
"""刷新天气并关闭对话框"""
|
|
|
|
|
self.on_refresh_clicked()
|
|
|
|
|
dialog.close()
|
|
|
|
|
|
|
|
|
|
def closeEvent(self, event):
|
|
|
|
|
"""窗口关闭事件 - 只是隐藏而不是销毁"""
|
|
|
|
|
self.closed.emit()
|
|
|
|
|
self.hide() # 隐藏窗口而不是销毁
|
|
|
|
|
event.ignore()
|
|
|
|
|
|
|
|
|
|
def show_at_position(self, x, y):
|
|
|
|
|
"""在指定位置显示窗口"""
|
|
|
|
|
self.move(x, y)
|
|
|
|
|
self.show()
|
|
|
|
|
|
|
|
|
|
def update_position(self, x, y):
|
|
|
|
|
"""更新窗口位置"""
|
|
|
|
|
self.move(x, y)
|