
前言
陶渊明《四时》
春水满四泽,
夏云多奇峰。
秋月扬明晖,
冬岭秀寒松。
看了古人的诗,我不禁也诗兴大发,想要吟诗一首:昨日平安夜,今天圣诞节,立冬没多久,马上要降温。额,回归正题啊,最近我的一些微信群里都在发江苏这边马上要迎来寒潮,要降温十几度的消息,突然就让我对天气变得比较敏感。现在想要查一下天气情况真的是太轻松了,打开手机,即可以在各种app上查到,于是我便想我们的组件库中是不是也可以添加一个天气控件,想到便开始做,于是便有了下面的效果。
效果展示






当然上面为了展示效果,除第一张多云的是真实的实况天气,后面几张都是我改过的天气,目前基本支持所有天气类型,我自己又将其分为如下29类:
//天气类型简化为如下分类,实在不想翻译了,就用数字代替了
enum WEATHER_TYPE
{
WEATHER_1, //晴
WEATHER_2, //少云
WEATHER_3, //晴间多云、多云
WEATHER_4, //阴
WEATHER_5, //有风、平静、微风、和风、清风
WEATHER_6, //强风/劲风、疾风、大风、烈风
WEATHER_7, //风暴、狂爆风、飓风、热带风暴
WEATHER_8, //龙卷风
WEATHER_9, //阵雨、强阵雨
WEATHER_10, //雷阵雨、雷阵雨伴有冰雹、强雷阵雨
WEATHER_11, //雨夹雪、雨雪天气、阵雨夹雪
WEATHER_12, //小雨、小雨-中雨、毛毛雨/细雨
WEATHER_13, //中雨、中雨-大雨
WEATHER_14, //大雨、大雨-暴雨
WEATHER_15, //暴雨、暴雨-大暴雨、大暴雨、特大暴雨、大暴雨-特大暴雨、极端降雨
WEATHER_16, //阵雪
WEATHER_17, //雪、小雪、小雪-中雪
WEATHER_18, //中雪、中雪-大雪
WEATHER_19, //大雪、大雪-暴雪
WEATHER_20, //暴雪
WEATHER_21, //雾、轻雾
WEATHER_22, //浓雾、强浓雾、大雾、特强浓雾
WEATHER_23, //霾、中度霾、重度霾、严重霾
WEATHER_24, //冻雨
WEATHER_25, //沙尘暴、强沙尘暴、
WEATHER_26, //浮尘、扬沙
WEATHER_27, //热
WEATHER_28, //冷
WEATHER_29, //未知
};
为了使每一类的天气状况更加形象具体,为了使控件更加的大气美观,如上面的动图所示,每一类都有特定的动画,共计29种动画。当然大家如果不喜欢这些默认的动画的话,也可以换成任意自己喜欢的图片或动画,只需要替换相应的资源即可。

天气查询功能如何实现?
要想制作实时天气控件,首先要能获取到实时天气状况,那么怎么获取实时天气状况呢?我们自然而然的想到是从网上获取,从某个网页上获取。因此我们首先要先找到能够为我们提供天气查询功能的网页,我从网上找到很多提供类似功能的网站,最后还是感觉如下两个网站比较靠谱,主要还是免费:
- 心知天气
- 高德地图天气查询
这两个网站都需要注册才能使用相关的api,其中高德地图每天查询上限是10万次,即使我们每秒查一次也才86400次,完全够用,请求格式如下,其中key的内容被我用xxxxxxxxx代替:
https://restapi.amap.com/v3/weather/weatherInfo?key=xxxxxxxxxx&extensions=base&city=南京
心知天气api好像是每秒限制20次,但是他免费的api返回的数据太少,只有天气类型和温度,其他一概没有,大家酌情自己选择。请求格式如下,其中key的内容被我用xxxxxxxxxx代替:
https://api.seniverse.com/v3/weather/now.json?key=xxxxxxxxxx&language=zh-Hans&unit=c&location=南京
有了能够获取数据的地方以后,那么该怎么通过Qt程序向网页请求数据呢?这时候就需要使用QtNetwork模块,其中主要用到如下三个类:
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
这三个类的具体作用我不再详解,大家可以自行了解一下,或者下次我再单独的讲解,主要功能就是向网页发送请求并处理回复,具体使用方法如下:
m_pManager = new QNetworkAccessManager(this);
connect(m_pManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
// 查询天气
void Weather::requestWeather()
{
QString strCity = m_strCityName; //城市名称
//char szQuest[256] = "https://api.seniverse.com/v3/weather/now.json?key=xxxxxxxxxx&language=zh-Hans&unit=c&location=";//心知天气api,每分钟20次好像,但是免费的接口返回的数据太少
char szQuest[256] = "https://restapi.amap.com/v3/weather/weatherInfo?key=xxxxxxxxxx&extensions=base&city="; //高德地图天气查询api,extensions=base表示实况天气,每日限值调用10万次
sprintf(szQuest, "%s%s", szQuest, strCity.toUtf8().data());
QNetworkRequest quest;
quest.setUrl(QUrl(szQuest));
quest.setHeader(QNetworkRequest::UserAgentHeader, "RT-Thread ART");
m_pManager->get(quest);
}
上面我们向网页发送请求以后,网页会给我返回相关的数据,数据一般都是JSON格式,以高德地图为例,其天气查询api返回的JSON数据格式如下:
{
"status": "1",
"count": "1",
"info": "OK",
"infocode": "10000",
"lives": [
{
"province": "江苏",
"city": "南京市",
"adcode": "320100",
"weather": "多云",
"temperature": "6",
"winddirection": "东",
"windpower": "≤3",
"humidity": "52",
"reporttime": "2026-03-18T20:35:59+00:00"
}
]
}
Qt也为我们提供了一些很方便的类来解析JSON数据:
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
JSON文档可以从它的基于文本的表示使用QJsonDocument::fromJson()转换为QJsonDocument,QJsonObject类用于封装JSON对象,QJsonArray类用于封装JSON数组。同样以解析上面JSON格式的天气数据为例,代码如下:
void Weather::replyFinished(QNetworkReply *reply)
{
QString strReply = reply->readAll();
//qDebug() << strReply;
//解析JSON
QJsonParseError error;
//转换为JSON文档
QJsonDocument document = QJsonDocument::fromJson(strReply.toUtf8(), &error);
//未发生错误就解析
if (!document.isNull() && (error.error == QJsonParseError::NoError))
{
//心知天气JSON解析
// if (document.isObject())
// {
// //转换为JSON对象
// QJsonObject object = document.object();
// if (object.contains("results"))
// {
// QJsonArray resultsArray = object["results"].toArray();
// foreach (const QJsonValue & value, resultsArray)
// {
// QJsonObject tempObj = value.toObject();
// if (tempObj.contains("now"))
// {
// QJsonValue nowValue = tempObj.value("now");
// QJsonObject nowItem = nowValue.toObject();
// qDebug()<< ZH_CN("天气 = ") << nowItem["text"].toString();
// qDebug()<< ZH_CN("温度 = ") << nowItem["temperature"].toString();
// }
// else
// qDebug() << "error 2";
// qDebug() << "last upadte = " << tempObj["last_update"].toString();
// }
// }
// else
// qDebug() << "error 1";
// }
//高德地图JSON解析
if (document.isObject())
{
//转换为JSON对象
QJsonObject object = document.object();
if (object.contains("lives"))
{
QJsonArray resultsArray = object["lives"].toArray();
foreach (const QJsonValue & value, resultsArray)
{
QJsonObject tempObj = value.toObject();
QString str;
//qDebug()<< ZH_CN("天气 = ") << tempObj["weather"].toString();
//qDebug()<< ZH_CN("温度 = ") << tempObj["temperature"].toString();
m_strWeather = tempObj["weather"].toString();
str = m_strWeather;
str += " ";
str += tempObj["temperature"].toString();
str += ZH_CN("°");
m_pWeather_Label->setText(str);
//qDebug()<< ZH_CN("风向 = ") << tempObj["winddirection"].toString();
//qDebug()<< ZH_CN("风力 = ") << tempObj["windpower"].toString();
str = tempObj["winddirection"].toString();
str += ZH_CN("风");
str += " ";
str += tempObj["windpower"].toString();
str += ZH_CN("级");
m_pWind_Label->setText(str);
//qDebug()<< ZH_CN("湿度 = ") << tempObj["humidity"].toString();
str = ZH_CN("湿度: ");
str += tempObj["humidity"].toString();
str += "%";
m_pHumidity_Label->setText(str);
//qDebug()<< ZH_CN("省份 = ") << tempObj["province"].toString();
//qDebug()<< ZH_CN("城市 = ") << tempObj["city"].toString();
//qDebug()<< ZH_CN("更新时间 = ") << tempObj["reporttime"].toString();
str = tempObj["province"].toString();
str += " ";
str += tempObj["city"].toString();
str += " ";
str += tempObj["reporttime"].toString();
m_pTime_Label->setText(str);
changeBackground(m_strWeather);
}
}
else
qDebug() << ZH_CN("天气数据解析失败!");
}
}
else
qDebug() << ZH_CN("请求天气数据是发生错误!");
}

页面及动态背景如何实现?
基本控件创建及窗口布局实现:
void Weather::initUI()
{
m_pBackground_Label = new QLabel(this);//背景图片标签
m_pBackground_Label->setScaledContents(true);
m_pWeather_Label = new QLabel(this); //天气和温度标签
m_pWind_Label = new QLabel(this); //风向和风力标签
m_pHumidity_Label = new QLabel(this); //湿度标签
m_pTime_Label = new QLabel(this); //天气更新时间和地名标签
QFont font("Microsoft YaHei", 16, QFont::Bold);
QPalette palette;
palette.setColor(QPalette::WindowText, QColor(255, 255, 255));
setLabelStyle(m_pWeather_Label, font, palette);
setLabelStyle(m_pWind_Label, font, palette);
setLabelStyle(m_pHumidity_Label, font, palette);
setLabelStyle(m_pTime_Label, QFont("Microsoft YaHei", 12, QFont::Bold), palette);
QWidget *bottom_Widget = new QWidget(this);
QVBoxLayout *bottom_VLayout = new QVBoxLayout();
bottom_VLayout->setMargin(0);
bottom_VLayout->addWidget(m_pBackground_Label);
bottom_Widget->setLayout(bottom_VLayout);
QWidget *top_Widget = new QWidget(this);
QVBoxLayout *top_VLayout = new QVBoxLayout();
top_VLayout->addWidget(m_pWeather_Label);
top_VLayout->addWidget(m_pWind_Label);
top_VLayout->addWidget(m_pHumidity_Label);
top_VLayout->addWidget(m_pTime_Label);
top_Widget->setLayout(top_VLayout);
top_Widget->setStyleSheet("QWidget { background: transparent; }");
QGridLayout *main_GLayout = new QGridLayout();
main_GLayout->setMargin(0);
main_GLayout->addWidget(bottom_Widget, 0, 0, 1, 1);
main_GLayout->addWidget(top_Widget, 0, 0, 1, 1);
this->setLayout(main_GLayout);
this->setStyleSheet("QWidget { background-color: QColor(56, 56, 56); }");
}
文字标签样式及文字阴影效果设置,文字阴影效果主要为了增加文字和背景的对比度,使文字更加清晰,易于分辨。
//设置标签样式
void Weather::setLabelStyle(QLabel *label, QFont font, QPalette palette)
{
label->setFont(font);
label->setPalette(palette);
//添加文字阴影
QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(label);
shadowEffect->setOffset(0, 0);
shadowEffect->setColor(Qt::black);
shadowEffect->setBlurRadius(10);
label->setGraphicsEffect(shadowEffect);
}
动态背景*放播**:
m_pMovie = new QMovie(strGifPath);
m_pBackground_Label->setMovie(m_pMovie);
m_pMovie->setSpeed(100);//0.5倍
m_pMovie->start();
