用qt显示天气 (qt气象控件)

qt开发天气预报,qt天气预报按钮

前言

陶渊明《四时》

春水满四泽,

夏云多奇峰。

秋月扬明晖,

冬岭秀寒松。

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

效果展示

qt开发天气预报,qt天气预报按钮

qt开发天气预报,qt天气预报按钮

qt开发天气预报,qt天气预报按钮

qt开发天气预报,qt天气预报按钮

qt开发天气预报,qt天气预报按钮

qt开发天气预报,qt天气预报按钮

当然上面为了展示效果,除第一张多云的是真实的实况天气,后面几张都是我改过的天气,目前基本支持所有天气类型,我自己又将其分为如下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种动画。当然大家如果不喜欢这些默认的动画的话,也可以换成任意自己喜欢的图片或动画,只需要替换相应的资源即可。

qt开发天气预报,qt天气预报按钮

天气查询功能如何实现?

要想制作实时天气控件,首先要能获取到实时天气状况,那么怎么获取实时天气状况呢?我们自然而然的想到是从网上获取,从某个网页上获取。因此我们首先要先找到能够为我们提供天气查询功能的网页,我从网上找到很多提供类似功能的网站,最后还是感觉如下两个网站比较靠谱,主要还是免费:

  • 心知天气
  • 高德地图天气查询

这两个网站都需要注册才能使用相关的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("请求天气数据是发生错误!");
}

qt开发天气预报,qt天气预报按钮

页面及动态背景如何实现?

基本控件创建及窗口布局实现:

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();

qt开发天气预报,qt天气预报按钮