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

QT项目——天气预报

文章目录

  • 前言
  • 一、项目介绍
  • 二、项目基础知识
    • 1. 软件开发网络通信架构
      • 1.1 CS架构 / BS架构
        • 1.1.1 CS架构(客户端-服务器架构)
        • 1.1.2 BS架构(浏览器-服务器架构)
      • 1.2 HTTP 基本概念
    • 2. QT 下 HTTP 编程
        • 2.1 类的解析
        • 2.2 示例程序
    • 3. JSON概念
    • 4. QT 下解析 JSON 数据
      • 4.1 QJsonDocument
      • 4.2 QJsonObject
      • 4.3 QJsonArray
  • 三、代码框架
    • 1. HTTP请求和判断是否连接
    • 2. 获得天气JSON数据
      • 2.1 刷新当天天气数据
      • 2.2 刷新当天天气图标
    • 3. 刷新七天天气
    • 4. 绘制温度曲线图
      • 4.1 事件过滤器
      • 4.2 绘制最高温度
      • 4.3 绘制最低温度

前言

完整代码:天气预报完整代码

一、项目介绍

本项目主要涉及 Qt 界面设计、HTTP 通信、JSON 数据解析、自定义控件绘制温湿度曲线、Qt 信号与槽等的应用,通过调用天气预报API解析 JSON 数据获取未来七天的温湿度、风力强度、天气状态等信息,最后显示在Qt UI界面。

界面展示:

image.png

二、项目基础知识

1. 软件开发网络通信架构

1.1 CS架构 / BS架构

在计算机网络和软件开发中,CS架构(Client-Server Architecture,客户端-服务器架构)和BS架构(Browser-Server Architecture,浏览器-服务器架构)是两种主要的应用程序架构。

1.1.1 CS架构(客户端-服务器架构)

CS架构是一种典型的两层结构,包括客户端和服务器两个部分。在这种架构中,客户端和服务器通过网络进行通信,每部分都有明确的职责。

  1. 客户端:
    • 用户界面通常在客户端呈现。
    • 可以是桌面应用程序、移动应用或专用软件。
    • 负责向服务器发送请求,接收和处理服务器响应。
  2. 服务器:
    • 管理数据和业务逻辑。
    • 处理来自客户端的请求,并发送回响应。
    • 通常承载在远程系统上,如数据库服务器、应用服务器等。
  3. 特点:
    • 需要为每种操作系统或平台单独开发客户端。
    • 高效的数据处理和响应能力。
    • 在客户端设备上占用资源(如内存和处理能力)。
1.1.2 BS架构(浏览器-服务器架构)

BS架构是一种基于Web的三层或多层架构,主要通过Web浏览器作为客户端访问服务器上的应用程序。

  1. 浏览器(客户端):
    • 使用标准Web浏览器(如Chrome、Firefox等)作为客户端。
    • 无需安装额外的软件,使用HTML、CSS和JavaScript显示内容。
  2. 服务器:
    • 和CS架构中的服务器类似,处理业务逻辑和数据存储。
    • 通过Web服务(如HTTP服务器)提供页面和数据。
  3. 特点:
    • 跨平台兼容性强,可以在任何支持Web浏览器的设备上运行。
    • 客户端无需安装专用软件,容易维护和更新。
    • 可能依赖网络性能,因为所有操作都在服务器上进行。

对比

  • 部署和维护:BS架构易于部署和维护,而CS架构通常需要在每个客户端单独安装和更新。
  • 性能:CS架构可以更有效地利用客户端的计算资源,适合高性能要求的应用。BS架构依赖于服务器的性能和网络延迟。
  • 安全性:CS架构中,数据经常在客户端和服务器之间传输,可能需要更复杂的安全措施。BS架构中,敏感数据主要存储在服务器端。
  • 用户体验:CS架构通常能提供更丰富的用户界面和交互功能。BS架构的用户体验受限于Web技术的能力。
    在实际应用中,选择哪种架构取决于具体的业务需求、目标用户群、性能要求以及开发和维护的成本。

1.2 HTTP 基本概念

基本概念

HTTP(超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是万维网 (WWW)的数据通信的基础。了解HTTP的基本概念对于理解现代网络通信至关重要。以下是HTTP的一 些核心概念:

1. 请求与响应

HTTP是一个基于请求-响应模式的协议。客户端(通常是Web浏览器)向服务器发送一个HTTP请求,然后服务器返回一个HTTP响应。请求包含请求的资源(如网页),而响应包含请求的资源的内容。

2. HTTP方法

HTTP定义了一系列的方法来表明对资源的不同操作,最常用的包括:

  • GET: 用于请求资源。

  • POST: 用于提交数据给服务器(例如,表单数据)。

  • PUT: 用于上传文件或内容。

  • DELETE: 用于请求删除资源。

  • HEAD: 用于获取资源的元信息,而不是资源本身。

3. 状态码

服务器对请求的响应中包含一个状态码,它表示请求的成功或失败,以及失败的原因。常见的状态码包括:

  • 200 OK: 请求成功。
  • 404 Not Found: 请求的资源未找到。
  • 500 Internal Server Error: 服务器内部错误。
  • 301 Moved Permanently: 请求的资源已永久移动到新位置。

4. URL(统一资源定位符)

URL是Web上资源的地址。它指定了资源的位置以及用于访问资源的协议(例如,http://)。

5. HTTP头

HTTP请求和响应包含头部信息,这些信息包括元数据,如内容类型、内容长度、服务器信息、客户端信息等。例如, Content-Type 头部指示响应中的媒体类型(如text/html,application/json)。

6. 无状态协议

HTTP是一个无状态协议,这意味着服务器不会保留任何请求的数据(状态)。然而,通过使用如 Cookies这样的机制,可以在多个请求之间维持状态。

7. 安全性(HTTPS)

HTTPS是HTTP的安全版本,它在HTTP和TCP层之间增加了一个加密层(通常是SSL/TLS)。这提供了数据传输的加密和更好的安全性。

8. RESTful API

RESTful是一种使用HTTP协议的Web服务设计风格,它利用HTTP的方法来实现API的不同操作。在RESTful架构中,每个URL代表一个资源,并使用HTTP的方法(如GET, POST)来处理这些资源。

9. Session和Cookies

由于HTTP本身是无状态的,Cookies和会话(Session)被用来在多个请求之间存储用户数据,从而为用户提供连贯的体验。

2. QT 下 HTTP 编程

Qt 中的 HTTP 编程主要涉及使用 Qt 的网络模块来进行 HTTP 请求和处理 HTTP 响应。Qt 提供了一系列类来处理网络通信,其中最常用的类是 QNetworkAccessManagerQNetworkRequestQNetworkReply 以及相关的支持类。

2.1 类的解析

1. QNetworkAccessManager

QNetworkAccessManager 类允许应用程序发送网络请求并接收回复。

网络访问应用程序编程接口(API)是围绕一个 QNetworkAccessManager 对象构建的,该对象保存着它所发送请求的通用配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号,还有可用于监控网络操作进度的回复信号。对于整个 Qt 应用程序来说,一个 QNetworkAccessManager 实例通常就足够了。由于 QNetworkAccessManager 基于 QObject,所以它只能在其所属的线程中使用。

一旦创建了一个 QNetworkAccessManager 对象,应用程序就可以使用它通过网络发送请求。提供了一组标准函数,这些函数接收一个请求以及可选的数据,并各自返回一个 QNetworkReply 对象。返回的这个对象用于获取针对相应请求所返回的任何数据。

一个简单的从网络下载的操作可以通过以下方式完成:

QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished,
        this, &MyClass::replyFinished);

manager->get(QNetworkRequest(QUrl("http://qt-project.org")));

QNetworkAccessManager 具有异步应用程序编程接口(API)。当上面的 replyFinished 槽函数被调用时,它所接收的参数是包含已下载数据以及元数据(头部信息等)的 QNetworkReply 对象。

注意:在请求完成之后,用户有责任在适当的时候删除 QNetworkReply 对象。不要在连接到 finished() 的槽函数内部直接删除它。你可以使用 deleteLater() 函数。

注意:QNetworkAccessManager 会对它接收到的请求进行排队。并行执行的请求数量取决于协议。目前,对于桌面平台上的 HTTP 协议,针对一个主机 / 端口组合,会并行执行 6 个请求。

2. QNetworkRequest

QNetworkRequest 类保存着一个要通过 QNetworkAccessManager 发送的请求。

QNetworkRequest 是网络访问应用程序编程接口(API)的一部分,它是一个用于保存通过网络发送请求所需信息的类。它包含一个 URL 以及一些可用于修改请求的辅助信息。

3. QNetworkReply

QNetworkReply 类包含了使用 QNetworkAccessManager 发送请求的数据和头部信息。

QNetworkReply 类还包含了与使用 QNetworkAccessManager 发布的请求相关的数据和元数据。与 QNetworkRequest 类似,它包含一个 URL 和 头部信息(既有解析后的形式,也有原始形式)、一些有关回复状态的信息以及回复本身的内容。

QNetworkReply 是一个顺序访问的 QIODevice,这意味着一旦从该对象读取了数据,设备就不会再保留这些数据。因此,如果应用程序需要这些数据,就有责任自行保存。每当从网络接收到更多数据并进行处理时,就会发出 readyRead() 信号。

当接收到数据时,也会发出 downloadProgress() 信号,但如果对内容进行了任何转换(例如,解压并去除协议开销),该信号中包含的字节数可能并不代表实际接收到的字节数。

尽管 QNetworkReply 是一个连接到回复内容的 QIODevice,但它也会发出 uploadProgress() 信号,该信号用于指示具有此类内容的操作的上传进度。

注意:不要在连接到 error()finished() 信号的槽函数中删除该对象。请使用 deleteLater()

2.2 示例程序
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
	// 创建一个QNetworkAccessManager 对象
    QNetworkAccessManager manager;
    
    // 请求的Url
    QNetworkRequest request(QUrl("http://example.com"));
    
	// 获取请求内容 返回一个QNetworkReply对象
    QNetworkReply *reply = manager.get(request);
	
    QObject::connect(reply, &QNetworkReply::finished, [&]() {
        if (reply->error()) {
            qDebug() << "Error:" << reply->errorString();
            return;
        }
        // 从设备中读取所有剩余数据,并将其作为字节数组返回。
        QString response = reply->readAll();
        qDebug() << "Response:" << response;
    });
    return a.exec();
}

3. JSON概念

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它易于人阅读和编写,同时也易于机器解析和生成。JSON是基于JavaScript的一个子集,尽管它是独立于语言的,且有多种语言支持。 JSON常用于网络应用程序中的数据传输,尤其是在Web应用程序中与后端服务器通信。 使用JSON的原因总结如下:

原因描述
易于阅读和编写JSON的结构简单、清晰,对人类来说易于阅读和编写。
轻量级数据格式相较于XML等标记语言,JSON更轻量,使用更少的符号,数据体积更小。
易于解析和生成大多数编程语言都提供了解析和生成JSON的内置支持或库。
跨语言支持JSON是独立于语言的,被广泛支持和使用在多种编程语言中。
网络友好JSON格式适合Web环境,易于通过网络传输,是Web API的常用格式。
数据互操作性作为一种标准化格式,JSON提高了不同系统间的数据互操作性。

BS/CS开发过程中,会使用不同的编程语言,JSON作为数据传输的标准化格式,方便程序员协议约定和数据处理,以下是不同编程语言处理JSON的方案

语言/平台JSON处理库/接口特点/描述
CJansson提供JSON的编码、解码和处理功能
C++nlohmann/json现代C++(从C++11开始)的JSON库,易于使用
javaJackson强大的JSON处理库,支持JSON的序列化和反序列化
GsonGoogle提供的JSON序列化/反序列化库
PythonjsonPython标准库中的JSON处理模块
QtQJsonDocumentQt框架中用于JSON处理的类
QJsonObject用于表示JSON对象的Qt类
QJsonArray用于表示JSON数组的Qt类
Androidorg.jsonAndroid SDK自带的JSON处理类,提供基础JSON操作功能
iOSJSONSerializationApple提供的用于JSON处理的类,部分Swift和Objective-C标准库中

4. QT 下解析 JSON 数据

在Qt中生成和解析JSON数据涉及使用QJsonDocumentQJsonObjectQJsonArray类。

4.1 QJsonDocument

QJsonDocument类提供了一种读写JSON文档的方法。

QJsonDocument是一个类,它封装了一个完整的JSON文档,并且能够以UTF-8编码的基于文本的表示形式以及Qt自身的二进制格式来读写该文档。

可以使用QJsonDocument::fromJson()将一个基于文本表示的JSON文档转换为QJsonDocumenttoJson()则可将其转换回文本形式。该解析器速度非常快且效率高,它会将JSON转换为Qt所使用的二进制表示形式。

可以通过!isNull()来查询已解析文档的有效性。

可以使用isArray()isObject()来查询一个文档是否包含数组或对象。文档中包含的数组或对象可以通过array()object()获取,然后进行读取或操作。

还可以使用fromBinaryData()fromRawData()从存储的二进制表示形式来创建一个文档。

4.2 QJsonObject

QJsonObject 类封装了一个 JSON 对象。

一个 JSON 对象是一个键值对的列表,其中键是唯一的字符串,而值由 QJsonValue 表示。

一个 QJsonObject 可以与一个 QVariantMap 相互转换。你可以使用 size() 查询(键,值)对的数量,使用 insert()remove() 对其中的条目进行插入和删除操作,并且可以使用标准的 C++ 迭代器模式遍历其内容。

QJsonObject 是一个隐式共享类,只要它未被修改,就会与其创建时所源自的文档共享数据。

你可以通过 QJsonDocument 将该对象与基于文本的 JSON 进行相互转换。

4.3 QJsonArray

QJsonArray 类封装了一个 JSON 数组。

一个 JSON 数组是一个值的列表。可以通过在数组中插入和删除 QJsonValue 来操作该列表。

一个 QJsonArray 可以与一个 QVariantList 相互转换。你可以使用 size() 查询条目的数量,使用 insert()removeAt() 对其中的条目进行插入和删除操作,并且可以使用标准的 C++ 迭代器模式遍历其内容。

QJsonArray 是一个隐式共享类,只要它未被修改,就会与其创建时所源自的文档共享数据。

你可以通过 QJsonDocument 将该数组与基于文本的 JSON 进行相互转换。

三、代码框架

1. HTTP请求和判断是否连接

流程图:

image.png

在这里用的天气API网址是易客云,直接百度搜索即可。

HTTP请求和判断代码示例:

#include <QNetworkReply>
#include <QMessageBox>
    
public slots:
    void readHttpRply();

private:
    QNetworkReply *reply;

Weather::Weather(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Weather)
{
    ui->setupUi(this);

    // http 请求
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QUrl urlItBoy("http://v1.yiketianqi.com/free/week?unescape=1&appid=18819752&appsecret=Telvfll9");

    // 获得数据
    QNetworkRequest res(urlItBoy);
    reply = manager->get(res);

    connect(reply, &QNetworkReply::finished, this, &Weather::readHttpRply);
}

void Weather::readHttpRply()
{   
    /* 从reply对象中获取HTTP状态码,并将其转换为整数 */
    int resCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    if (reply->error() == QNetworkReply::NoError && resCode == 200)
    {
        QByteArray data = reply->readAll();
        qDebug() << QString::fromUtf8(data);
    }
    else
    {
        QMessageBox mes;
        mes.setWindowTitle("错误");
        mes.setText("网络请求失败");
        mes.setStyleSheet("QPushButton {color:red}");
        mes.setStandardButtons(QMessageBox::Ok);
        mes.exec();
    }
}

image.png

编译运行结果:获得当天的天气

image.png

2. 获得天气JSON数据

2.1 刷新当天天气数据

通过解析天气 API 的 JSON 数据,读取到 UI 界面。

代码示例:

#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>

private:
    void parseWeatherJsonData(QByteArray rawData);

void Widget::parseWeatherJsonData(QByteArray rawData)
{
    QJsonDocument jsonObj = QJsonDocument::fromJson(rawData);
    if (!jsonObj.isNull() && jsonObj.isObject())
    {
        QJsonObject objRoot = jsonObj.object();
        
        //解析日期1
        QString date = objRoot["date"].toString();
        QString week = objRoot["week"].toString();
        ui->labelCurrentDate->setText(date+"  "+week);
        
        //解析城市名称
        QString cityName = objRoot["city"].toString();
        ui->labelCity->setText(cityName+"市");
        
        //解析当前温度
        QString currentTemp = objRoot["tem"].toString();
        ui->labelTmp->setText(currentTemp+"℃");
        ui->labelTempRange->setText(objRoot["tem2"].toString()+"~"
                +objRoot["tem1"].toString());
                
        //解析天气类型
        ui->labelweatherType->setText(objRoot["wea"].toString());
        
        //感冒指数
        ui->labelGanmao->setText(objRoot["air_tips"].toString());
        
        //风向
        ui->labelFXType->setText(objRoot["win"].toString());
        
        //风力
        ui->labelFXTypeData->setText(objRoot["win_speed"].toString());
        
        //PM2.5
        ui->labelPM25Data->setText(objRoot["air_pm25"].toString());
        
        //湿度
        ui->labelShiduData->setText(objRoot["humidity"].toString());
        
        //空气质量
        ui->labelairData->setText(objRoot["air_level"].toString());
    }
}

void Widget::readHttpRply()
{
    int resCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    if (reply->error() == QNetworkReply::NoError && resCode == 200)
    {
        QByteArray data = reply->readAll();
        parseWeatherJsonData(data);
        qDebug() << QString::fromUtf8(data);
    }
    else
    {
        QMessageBox mes;
        mes.setWindowTitle("错误");
        mes.setText("网络请求失败");
        mes.setStyleSheet("QPushButton {color:red}");
        mes.setStandardButtons(QMessageBox::Ok);
        mes.exec();
    }
}

2.2 刷新当天天气图标

将所有天气图标存放到 QMap 容器中,模板参数为<QString,QString>

代码示例:

private:
    QMap<QString,QString> mTypeMap;

Weather::Weather(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Weather)
{
    //根据keys,设置icon的路径
    mTypeMap.insert("暴雪",":/images/type/BaoXue.png");
    mTypeMap.insert("暴雨",":/images/type/BaoYu. png");
    mTypeMap.insert("暴雨到大暴雨",":/images/type/BaoYuDaoDaBaoYu.png");
    mTypeMap.insert("大暴雨",":/images/type/DaBaoYu.png");
    mTypeMap.insert("大暴雨到特大暴雨",":/images/type/DaBaoYuDaoTeDaBaoYu.png");
    mTypeMap.insert("大到暴雪",":/images/type/DaDaoBaoXue.png");
    mTypeMap.insert("大雪",":/images/type/DaXue.png");
    mTypeMap.insert("大雨",":/images/type/DaYu.png");
    mTypeMap.insert("冻雨",":/images/type/DongYu.png");
    mTypeMap.insert("多云",":/images/type/DuoYun.png");
    mTypeMap.insert("浮沉",":/images/type/FuChen.png");
    mTypeMap.insert("雷阵雨",":/images/type/LeiZhenYu.png");
    mTypeMap.insert("雷阵雨伴有冰雹",":/images/type/LeiZhenYuBanYouBingBao.png");
    mTypeMap.insert("霾",":/images/type/Mai.png");
    mTypeMap.insert("强沙尘暴",":/images/type/QiangShaChenBao.png");
    mTypeMap.insert("晴",":/images/type/Qing.png");
    mTypeMap.insert("沙尘暴",":/images/type/ShaChenBao.png");
    mTypeMap.insert("特大暴雨",":/images/type/TeDaBaoYu.png");
    mTypeMap.insert("undefined",":/images/type/undefined.png");
    mTypeMap.insert("雾",":/images/type/Wu.png");
    mTypeMap.insert("小到中雪",":/images/type/XiaoDaoZhongXue.png");
    mTypeMap.insert("小到中雨",":/images/type/XiaoDaoZhongYu.png");
    mTypeMap.insert("小雪",":/images/type/XiaoXue.png");
    mTypeMap.insert("小雨",":/images/type/XiaoYu.png");
    mTypeMap.insert("雪",":/images/type/Xue.png");
    mTypeMap.insert("扬沙",":/images/type/YangSha.png");
    mTypeMap.insert("阴",":/images/type/Yin.png");
    mTypeMap.insert("雨",":/images/type/Yu.png");
    mTypeMap.insert("雨夹雪",":/images/type/YuJiaXue.png");
    mTypeMap.insert("阵雪",":/images/type/ZhenXue.png");
    mTypeMap.insert("阵雨",":/images/type/ZhenYu.png");
    mTypeMap.insert("中到大雪",":/images/type/ZhongDaoDaXue.png");
    mTypeMap.insert("中到大雨",":/images/type/ZhongDaoDaYu.png");
    mTypeMap.insert("中雪",":/images/type/ZhongXue.png");
    mTypeMap.insert("中雨",":/images/type/ZhongYu.png");
}

void Widget::parseWeatherJsonData(QByteArray rawData)
{
    if (!jsonObj.isNull() && jsonObj.isObject())
    {
        //解析天气类型
        ui->labelWeatherIcon->setPixmap(mTypeMap[objRoot["wea"].toString()]);
        ui->labelweatherType->setText(objRoot["wea"].toString());
    }
}

编译运行结果:

image.png

3. 刷新七天天气

定义一个 QList 容器,模板参数为<QLabel *>,用来存放每日的天气情况。

代码示例:day.h

#ifndef DAY_H
#define DAY_H

#include <QString>

class Day
{
public:
    Day();

    QString mDate;
    QString mWeek;
    QString mCity;
    QString mTemp;
    QString mWeathType;
    QString mTempLow;
    QString mTempHigh;

    QString mTips;
    QString mFx;
    QString mFl;
    QString mPm25;
    QString mHu;
    QString mAirq;
};

#endif // DAY_H

核心代码:

public:
    Day days[7];

    QList<QLabel *> mDateList;
    QList<QLabel *> mWeekList;
    QList<QLabel *> mIconList;
    QList<QLabel *> mWeaTypeList;
    QList<QLabel *> mAirqList;
    QList<QLabel *> mFxList;
    QList<QLabel *> mFlList;
    
private:
    void parseWeatherJsonDataNew(QByteArray rawData);
    void updateUI();
    
Weather::Weather(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Weather)
{
    
    mWeekList << ui->labelday0 << ui->labelday1
              << ui->labelday2 << ui->labelday3
              << ui->labelday4 << ui->labelday5;
    mDateList << ui->labelDate0 << ui->labelDate1
              << ui->labelDate2 << ui->labelDate3
              << ui->labelDate4 << ui->labelDate5;
    mIconList << ui->labelWeaterIcon0 << ui->labelWeaterIcon1
              << ui->labelWeaterIcon2 << ui->labelWeaterIcon3
              << ui->labelWeaterIcon4 << ui->labelWeaterIcon5;
    mWeaTypeList << ui->lbweatherTypeDate0 << ui->lbweatherTypeDate1
                 << ui->lbweatherTypeDate2 << ui->lbweatherTypeDate3
                 << ui->lbweatherTypeDate4 << ui->lbweatherTypeDate5;
    mAirqList << ui->labelairq0 << ui->labelairq1 << ui->labelairq2
              << ui->labelairq3 << ui->labelairq4 << ui->labelairq5;
    mFxList << ui->labelFX0 << ui->labelFX1 << ui->labelFX2
            << ui->labelFX3 << ui->labelFX4 << ui->labelFX5;
    mFlList << ui->labelFL0 << ui->labelFL1 << ui->labelFL2
            << ui->labelFL3 << ui->labelFL4 << ui->labelFL5;
}
    
    
// 解析json数据 七天数据
void Weather::parseWeatherJsonDataNew(QByteArray rawData)
{
    // 将JSON作为UTF-8编码的JSON文档进行解析,并基于它创建一个 QJsonDocument
    QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);
    // 解析对象
    if(!jsonDoc.isNull() && jsonDoc.isObject()){
        QJsonObject jsonRoot = jsonDoc.object();
        days[0].mCity = jsonRoot["city"].toString();
        days[0].mPm25 = jsonRoot["aqi"].toObject()["pm25"].toString();
        // 解析数组
        if(jsonRoot.contains("data") && jsonRoot["data"].isArray()){
            // 转换类型为QT表示JSON数组的数据类型
            QJsonArray weaArray = jsonRoot["data"].toArray();
            for(int i = 0; i < weaArray.size(); i++){
                // 转换类型为QT表示JSON对象的数据类型
                QJsonObject obj = weaArray[i].toObject();
                days[i].mDate = obj["date"].toString();
                days[i].mWeek = obj["week"].toString();
                days[i].mWeathType = obj["wea"].toString();
                days[i].mTemp = obj["tem"].toString();
                days[i].mTempLow = obj["tem2"].toString();
                days[i].mTempHigh = obj["tem1"].toString();
                days[i].mFx = obj["win"].toArray()[0].toString();
                days[i].mFl = obj["win_speed"].toString();
                days[i].mAirq = obj["air_level"].toString();
                days[i].mTips = obj["air_tips"].toString();
                days[i].mHu = obj["humidity"].toString();

            }
        }
    }
    // 更新界面
    updateUI();
}

void Weather::updateUI()
{
    QPixmap pixmap;
    // 解析当前日期
    ui->labelCurrentDate->setText(days[0].mDate + " " + days[0].mWeek);
    // 解析城市名词
    ui->labelCity->setText(days[0].mCity + "市");
    // 解析当前温度
    ui->labelTmp->setText(days[0].mTemp + "℃");
    ui->labelTempRange->setText(
                days[0].mTempLow + "℃" + "~"  + days[0].mTempHigh +  "℃");
    // 解析天气类型
    ui->labelweatherType->setText(days[0].mWeathType);
    ui->labelWeatherIcon->setPixmap(mTypeMap[days[0].mWeathType]);
    // 解析感冒指数
    ui->labelGanmao->setText(days[0].mTips);
    // 解析风向
    ui->labelFXType->setText(days[0].mFx);
    // 解析风力
    ui->labelFXTypeData->setText(days[0].mFl);
    // 解析PM2.5
    ui->labelPM25Data->setText(days[0].mPm25);
    // 解析湿度
    ui->labelShiduData->setText(days[0].mHu);
    // 解析空气质量
    ui->labelairData->setText(days[0].mAirq);
    ui->lbweatherTypeDate3->setText(days[3].mWeathType);
    ui->lbweatherTypeDate0->setText(days[0].mWeathType);
    for(int i = 0; i < 6; i++){

        mWeekList[i]->setText(days[i].mWeek);
        mWeekList[0]->setText("今天");
        mWeekList[1]->setText("明天");
        mWeekList[2]->setText("后天");
        QStringList dayList = days[i].mDate.split("-");
        mDateList[i]->setText(dayList.at(1) + "-" + dayList.at(2));
        // 缩放图片大小和label大小能匹配
        int index = days[i].mWeathType.indexOf("转");
        if(index != -1)
        {
            pixmap = mTypeMap[days[i].mWeathType.left(index)];
        }
        else
        {
            pixmap = mTypeMap[days[i].mWeathType];
        }
        pixmap = pixmap.scaled(mIconList[i]->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
        mIconList[i]->setMaximumHeight(50);
        mIconList[i]->setMaximumWidth(ui->widget02->width()/6.5);
        mIconList[i]->setPixmap(pixmap);
        mWeaTypeList[i]->setText(days[i].mWeathType);
        QString airQ = days[i].mAirq;
        mAirqList[i]->setText(airQ);

        if(airQ == "优")
        {
            mAirqList[i]->setStyleSheet("background-color:rgb(150, 213, 32);border-radius: 7px;color: rgb(230, 230, 230)");
        }
        if(airQ == "良")
        {
            mAirqList[i]->setStyleSheet("background-color:rgb(255, 170, 127);border-radius: 7px;color: rgb(230, 230, 230)");
        }
        if(airQ == "轻度污染")
        {
            mAirqList[i]->setStyleSheet("background-color:rgb(170, 200, 129);border-radius: 7px;color: rgb(230, 230, 230)");
        }
        if(airQ == "中度污染")
        {
            mAirqList[i]->setStyleSheet("background-color:rgb(255, 17, 17);border-radius: 7px;color: rgb(230, 230, 230)");
        }
        if(airQ == "重度污染")
        {
            mAirqList[i]->setStyleSheet("background-color:rgb(153, 0, 0);border-radius: 7px;color: rgb(230, 230, 230)");
        }

        mFxList[i]->setText(days[i].mFx);
        index = days[i].mFl.indexOf("转");
        if(index != -1){
            mFlList[i]->setText(days[i].mFl.left(index));
        }else{
            mFlList[i]->setText(days[i].mFl);
        }
    }
}
    

编译运行结果:

image.png

4. 绘制温度曲线图

4.1 事件过滤器

API原型:

bool QObject::eventFilter(QObject *watched, QEvent *event)

如果此对象已被安装为受监视对象的事件过滤器,则对事件进行筛选。

在重新实现此函数时,如果你想要滤除该事件,即阻止它被进一步处理,返回 “真”;否则返回 “假”。

QObject 对象安装事件过滤器:对象用 installEventFilter() 后,所有达到目标控件的事件都首先到达监视对象的 eventFilter() 函数。如果一个对象有多个事件过滤器,过滤器按顺序激活,先到达最近安装的监视对象,最后到达最先安装的监视对象。

代码示例:

protected:
    bool eventFilter(QObject *watched,QEvent *event);

Weather::Weather(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Weather)
{
    // 安装事件过滤器
    ui->widget0404->installEventFilter(this);
    ui->widget0405->installEventFilter(this);
}

// watched 受监控对象 event 过滤的事件
bool Weather::eventFilter(QObject *watched, QEvent *event)
{
	// 满足条件则执行相应操作
    if(watched == ui->widget0404 && event->type() == QEvent::Paint)
    {
        drawTempLineHigh();
        return true;
    }
    
    if(watched == ui->widget0405 && event->type() == QEvent::Paint)
    {
        drawTempLineLow();
        return true;
    }
    
    return QWidget::eventFilter(watched,event);
}

4.2 绘制最高温度

整体思路:

选取一个参照物,算出其 x 轴位置,算出组件的中间位置,温度的平均值,根据获取到的温度减去平均值得到的值乘以 2 获得偏移值,最后结合组件的中间值和偏移值算出 y 轴位置,描点、连线。

代码示例:

void Weather::drawTempLineHigh()
{
    QPainter painter(ui->widget0404);
    // 抗锯齿
    painter.setRenderHint(QPainter::Antialiasing,true); 
    // 设置画刷
    painter.setBrush(Qt::yellow);
    // 设置画笔
    painter.setPen(Qt::yellow);

    int ave;  // 平均值
    int sum = 0; // 总数
    int offSet = 0; // 偏移值
    int middle = ui->widget0404->height()*0.7; // 中间位置
    for (int i = 0; i < 6; i++) 
    {
        sum += days[i].mTempHigh.toInt();
    }
    ave = sum / 6;
    
    // 定义6个点
    QPoint points[6];
    for (int i = 0; i < 6; i++) 
    {
          // 以mAirqList为参照算出 x 位置
          points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2);
          // 算出偏移量 (最高温度 - 平均温度) * 2
          offSet = (days[i].mTempHigh.toInt() - ave)*2;
          // 算出 Y 位置 中值 - 偏移量
          points[i].setY(middle - offSet);
          // 画出6个点
          painter.drawEllipse(QPoint(points[i]),3,3);
          // 画出当天温度
          painter.drawText(points[i].x() - 15,points[i].y()-15,days[i].mTempHigh + "°");
    }
    for (int i = 0; i < 5; i++) 
    {
        // 点与点之间连线
        painter.drawLine(points[i],points[i+1]);
    }
}

4.3 绘制最低温度

代码示例:

// 绘制最低温度曲线
// 绘制最低温度曲线
void Weather::drawTempLineLow()
{
    QPainter painter(ui->widget0405);
    // 抗锯齿
    painter.setRenderHint(QPainter::Antialiasing,true);
    // 设置画刷
    painter.setBrush(QColor(70,192,203));
    // 设置画笔
    painter.setPen(QColor(70,192,203));

    int ave;
    int sum = 0;
    int offSet = 0;
    int middle = ui->widget0405->height()*0.7;
    for (int i = 0; i < 6; i++) 
    {
        sum += days[i].mTempLow.toInt();
    }
    ave = sum / 6;

    // 定义6个点
    QPoint points[6];
    for (int i = 0; i < 6; i++) 
    {
          points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2);
          offSet = (days[i].mTempLow.toInt() - ave)*2;
          points[i].setY(middle - offSet);
          // 画出6个点
          painter.drawEllipse(QPoint(points[i]),3,3);
          // 画出当天温度
          painter.drawText(points[i].x() - 15,points[i].y()-15,days[i].mTempLow + "°");
    }
    for (int i = 0; i < 5; i++) 
    {
        // 画出两点之间的线
        painter.drawLine(points[i],points[i+1]);
    }
}

编译运行结果:

image.png


http://www.kler.cn/a/555656.html

相关文章:

  • 南凌科技接入deepseek大模型,提升云网智安服务能力
  • CE RED 增加网络安全 添加新网络安全类型
  • [Android]如何判断当前APP是Debug还是Release环境?
  • Java+SpringBoot+Vue+数据可视化的航班购票出行服务平台(程序+论文+讲解+安装+调试+售后)
  • RocketMQ - 常见问题
  • Unity Shader Graph 2D - Procedural程序化图形之夹心圆环
  • 网络设备的数据平面和控制平面
  • HtML之JavaScript BOM编程
  • 机器学习_13 决策树知识总结
  • 玩转Docker | 使用Docker部署本地自托管reference速查表工具
  • LayUi点击查看图片组件layer.photos()用法(图片放大预览后滚动鼠标缩放、底部显示自定义标题)
  • 【大模型系列篇】DeepSeek-R1如何通过强化学习有效提升大型语言模型的推理能力?
  • kafka-保姆级配置说明(producer)
  • Spring Boot 3.x 对 GraalVM 原生编译的深度支持
  • leetcode1047-删除字符串中的所有相邻重复项
  • 【量化科普】Sharpe Ratio,夏普比率
  • @Transactional 嵌套,内层抛异常,外层用 try-catch 捕获但实际事务却回滚了
  • 设计模式在Qt中的应用
  • QT--QLineEdit
  • Qt的QToolButton样式设置