【C++ QT项目5】——基于HTTP与JSON数据流的天气预报界面设计

【C++ QT项目5】——基于HTTP与JSON数据流的天气预报界面设计

  • 一、项目概述
  • 二、UI设计与stylesheet样式表
  • 三、天气预报数据接口
  • 四、JSON数据
    • 4.1 概述
    • 4.2 QT生成JSON数据
    • 4.3 QT解析JSON数据
    • 4.4 将JSON数据解析到QMap中
  • 五、软件开发网络通信架构
    • 5.1 BS架构/CS架构
    • 5.2 HTTP基本概念
    • 5.3 Qt中的HTTP编程
  • 6. 天气预报项目整合
    • 6.1 连接请求解析JSON数据并刷新界面
    • 6.2 支持不同城市查询的实现
    • 6.3 七日温度曲线的绘制

一、项目概述

设计一个天气预报界面,实现对不同城市区域的天气获取与显示,其中主要涉及

  • stylesheet界面美化
  • HTTP通信
  • JSON数据解析
  • 自定义控件绘制温度
  • 多控件及其最终代码整合调试能力。
    在这里插入图片描述

二、UI设计与stylesheet样式表

界面Layout设计
在这里插入图片描述
上下两部分命名规则
在这里插入图片描述在这里插入图片描述

stylesheet样式表美化

设置边框弧度

border-radius: 4px;

设置某方向边框弧度

border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;

设置背景颜色

background-color: rgba(60, 60, 60, 100);

设置字体颜色

color: rgb(230, 230, 230);

设置字体大小

font: 45pt;

设置按钮的三态

QPushButton#button1{
	background-color: rgba(220, 250, 220, 255);
	border:10px solid white;
	border-radius:32px;
	font-size:60px;
}
QPushButton#button1:hover{ 
	background-color: rgba(255, 255, 0, 255);
	border:10px solid white;
	border-radius:32px;
	color:#FF00FE;
	font-size:60px;
}
QPushButton#button1:pressed{
	background-color: rgba(255, 255, 0, 255);
	border:10px solid white;
	border-radius:32px;
	color: #FF00FE;
	font-size:60px;
} 

为按钮/标签添加背景图片
在这里插入图片描述

父控件影响
父控件指定某类控件的样式,子控件都要遵守此样式进行显示,除非子控件内部有做相关修改

QLabel 
{
	background-color: rgba(0, 200, 200, 200);
	border-radius: 4px;
}

三、天气预报数据接口

以下API网站返回的数据实际是JSON格式的数据,可以利用 该网站对数据进行可视化解析展示:JSON在线解析及格式化验证 - JSON

第一种:

  • http://t.weather.itboy.net/api/weather/city/101010100

数据返回:
在这里插入图片描述

第二种:
在这里插入图片描述
获取IP地址所属地的一日天气状况

  • http://v1.yiketianqi.com/api?unescape=1&version=v61&appid=89361488&appsecret=K6tTmCT0

获取特定城市的天气状况

  • http://v1.yiketianqi.com/api?unescape=1&version=v61&appid=89361488&appsecret=K6tTmCT0&cityid=实际城市的ID值

获取特定城市的未来7日天气状况

  • http://v1.yiketianqi.com/api?unescape=1&version=v9&appid=89361488&appsecret=K6tTmCT0&cityid=实际城市的ID值

例如:http://v1.yiketianqi.com/api?unescape=1&version=v9&appid=89361488&appsecret=K6tTmCT0&cityid=101040100

返回一日的天气数据:
在这里插入图片描述
返回七日天气数据:
在这里插入图片描述

四、JSON数据

4.1 概述

  JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,以键值对的形式存在。它易于人阅读和编写,同时也易于机器解析和生成。SON常用于网络应用程序中的数据传输,尤其是在Web应用程序中与后端服务器通信。
使用JSON的原因总结如下:

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

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

4.2 QT生成JSON数据

  在Qt中生成JSON数据并将其保存到文件的一个基本示例涉及使用 QJsonDocument 、 QJsonObject 和QJsonArray 类。以下是一组简单的JSON数据,通过程序创建一个简单JSON对象并将对应的JSON数据保存到相应的JSON文件中的示例代码。

JSON数据

{
    "cityId": 1010100,
    "date": "2024-02-22",
    "jsonArray": [
        "data1",
        "data2",
        100
    ],
    "weather": "雨夹雪"
}

示例代码:

void creatJsonFile()
{
    // 创建一个JSON对象 键值对
    QJsonObject rootObj;
    rootObj["cityId"] = 1010100;
    rootObj["date"] = "2024-02-22";
    rootObj["weather"] = "雨夹雪";

    // 创建一个JSON数组
    QJsonArray jsonArray;
    jsonArray.append("data1");
    jsonArray.append("data2");
    jsonArray.append(100);
    // 将数组添加到JSON对象
    rootObj["jsonArray"] = jsonArray;

	/****数据加入对象其他业务程序*****/


	/****数据加入对象其他业务程序*****/
	
    // 将JSON对象转换为JSON文档
    QJsonDocument jsonDoc(rootObj);
    // 将JSON文档转换为字符串(也可以是压缩格式)
    QByteArray datArray = jsonDoc.toJson();

    // 将JSON数据写入文件
    QFile file("E:/qtProject/00_QT_CLC/weather/test.json");
    if(!file.open(QIODevice::ReadWrite)){
        qDebug() << "---------file open error----------";
    }
    file.write(datArray);
    file.close();
}

说明

  1. 创建JSON对象:使用 QJsonObject 来构建JSON对象,并使用键值对填充数据。
  2. 创建JSON数组:使用 QJsonArray 来创建一个数组,并添加元素。
  3. 组合JSON结构:将JSON数组添加到JSON对象中。
  4. 生成JSON文档:通过 QJsonDocument 来处理JSON数据,可以选择格式化(缩进)或压缩形式。
  5. 保存到文件:创建 QFile 对象,打开文件,写入JSON数据,并关闭文件。

上述例子展示了Qt中处理JSON的基础流程,包括创建、填充数据、转换为字符串,以及写入文件。

补充:
  也可以根据需要调整这个流程来适应更复杂的JSON结构或数据,即在对象中加入子对象,在JSON数组中,数组的每一项均为对象类型,其需要增加的数据如下红框所示。
在这里插入图片描述

其中再次添加上述数据的程序如下所示:

	/****数据加入对象其他业务程序*****/
	// 创建一个JSON 子对象并添加到JSON对象
	QJsonObject alarmObj;
	alarmObj["alarm_type"] = "雨夹雪";
	alarmObj["alarm_level"] = "黄色";
	alarmObj["alarm_context"] = "杭州市发布雨夹雪黄色预警";
	rootObj["alarm"] = alarmObj;
	
	//创建一个数组对象,其中数组每一项均为子对象
	QJsonArray daysArray;
	QJsonObject day0;
	day0["day"] = "星期一";
	day0["data"] = "2024-02-22";
	day0["weather"] = "晴";
	day0["temp"] = 12;
	//在数组中添加子对象
	daysArray.append(day0);
	
	QJsonObject day1;
	day1["day"] = "星期二";
	day1["data"] = "2024-02-23";
	day1["weather"] = "多云";
	day1["temp"] = 10;
	daysArray.append(day1);
	
	QJsonObject day2;
	day2["day"] = "星期三";
	day2["data"] = "2024-02-24";
	day2["weather"] = "晴";
	day2["temp"] = 15;
	daysArray.append(day2);
	
	//将JSON数据添加至根对象
	rootObj["days"] = daysArray;
	/****数据加入对象其他业务程序*****/

  在JSON中,数组可以包含多种类型的元素,包括对象。当在Qt中处理JSON数组,其中的元素是对象时,使用 QJsonArrayQJsonObject 来创建和处理这些数据结构。以上就是一个示例,展示了如何创建一个包含多个对象的JSON数组,并将该数组添加到一个JSON对象中。

4.3 QT解析JSON数据

  在Qt中解析JSON数据通常涉及到使用 QJsonDocument 、 QJsonObject 和 QJsonArray 类。这些类提供了处理JSON数据的必要工具,使您能够从JSON字符串中提取信息、遍历JSON对象或数组,并访问具体的数据项。以下是一个基本的示例,展示了如何在Qt中解析JSON字符串。

示例:解析JSON字符串
假设您有一个JSON字符串,例如:

{
    "cityId": 1010100,
    "date": "2024-02-22",
    "jsonArray": [
        "data1",
        "data2",
        100
    ],
    "weather": "雨夹雪"
}

以下是如何在Qt中解析这个JSON字符串的步骤:

void Widget::readJsonFile()
{
    // 读取JSON文件数据
    QFile file("E:/qtProject/00_QT_CLC/weather/data.json");
    if(!file.open(QIODevice::ReadOnly)){
        qDebug() << "---------readJsonFile file open error----------";
    }
    QString srcData = file.readAll();
    file.close();

    // 将JSON字符串转换为QJsonDocument
    QJsonDocument jsonDoc = QJsonDocument::fromJson(srcData.toUtf8());
    //JSON文件不为空 且是一个对象
    if (!jsonDoc.isNull() && jsonDoc.isObject())
    {
        //创建根对象
        QJsonObject rootObj = jsonDoc.object();
        //获取根对象下的属性
        qDebug() << "----------获取根对象下的属性-----------";
        int cityId  = rootObj["cityId"].toInt();
        QString date    = rootObj["date"].toString();
        QString weather = rootObj["weather"].toString();
        qDebug() << "cityId:" << cityId;
        qDebug() << "date:" << date;
        qDebug() << "weather:" << weather;


        //获取数组jsonArray数据
        int i = 0;
        if(rootObj.contains("jsonArray") && rootObj["jsonArray"].isArray())
        {
            QJsonArray jsonArray = rootObj["jsonArray"].toArray();
            qDebug() << "----------获取数组jsonArray数据-----------";
            for(QJsonValue arr : jsonArray)
            {
                //qDebug() << arr;
                //判断对象的类型 根据不同的类型进行转化
                switch (arr.type())
                {
                    case QJsonValue::String:    qDebug() << "data" << i << ":" << arr.toString();   break;
                    case QJsonValue::Double:    qDebug() << "data" << i << ":" << arr.toDouble();   break;
                    case QJsonValue::Array:break;
                    case QJsonValue::Object:break;
                    case QJsonValue::Bool:      qDebug() << "data" << i << ":" << arr.toBool();     break;
                    default:break;
                }
                i++;
            }
         }
            
    }
}

说明

  • 字符串转换为 QJsonDocument :使用 QJsonDocument::fromJson 方法将JSON字符串转换为QJsonDocument 对象。
  • 提取 QJsonObject :如果 QJsonDocument 包含一个JSON对象,使用 object() 方法获取它。
  • 访问对象数据:使用键(如 "name""age")访问 QJsonObject 中的数据。
  • 处理数组:如果对象包含一个数组,使用 QJsonArray 来遍历数组中的元素。

这个示例提供了一个基础框架,用于在Qt中解析和处理JSON数据。您可以根据实际需要调整这个过程,以适应不同的JSON结构和数据类型。

补充:
  解析对象中含有子对象与数组的每一项为对象的解析程序
数据:
在这里插入图片描述
解析程序:

        //获取alarm子对象数据
        if(rootObj.contains("alarm") && rootObj["alarm"].isObject())
        {
            QJsonObject alarm = rootObj["alarm"].toObject();
            qDebug() << "----------获取alarm子对象数据-----------";
            qDebug() << "alarm_type: " << alarm["alarm_type"].toString();
            qDebug() << "alarm_level: " << alarm["alarm_level"].toString();
            qDebug() << "alarm_context: " << alarm["alarm_context"].toString();
        }
        
        //获取数组对象days数据
        if(rootObj.contains("days") && rootObj["days"].isArray())
        {
            QJsonArray dayArray = rootObj["days"].toArray();
            qDebug() << "----------获取数组对象days数据-----------";
            //遍历数组 遍历的每个为obj对象
            for(QJsonValue day : dayArray)
            {
                if(day.type() == QJsonValue::Object)
                {
                    QString dataStr    =  day["data"].toString();
                    QString dayStr     =  day["day"].toString();
                    QString weatherStr =  day["weather"].toString();
                    int     tempature  =  day["temp"].toInt();
                    qDebug() << "data:" << dataStr << "day:" << dayStr << "temp:" << tempature  << "weather:" << weatherStr;
                }
            }
        }

4.4 将JSON数据解析到QMap中

  在Qt中,如果你想要将JSON数据解析到一个 QMap 中,你可以遍历JSON对象的所有键值对,并将它们添加到 QMap 里。这个方法特别适合于当你的JSON对象是一个简单的键值对集合时。以下是一个如何实现这一点的示例。

示例:将JSON数据解析到QMap中
假设有以下JSON数据:

json
{
  "name": "John Doe",
  "age": "30",
  "email": "john.doe@example.com"
}

以下是如何将这些数据解析到 QMap<QString, QString> 中的步骤:

oid parseJsonToMap() 
{
	// JSON字符串
	QString jsonString = R"(
	  {
		"name": "John Doe",
		"age": "30",
		"email": "john.doe@example.com"
	  }
	)";
	
	// 将JSON字符串转换为QJsonDocument
	QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8());
	
	// 准备一个QMap来存储解析的数据
	QMap<QString, QString> dataMap;
	// 解析JSON对象并填充QMap
	if (!jsonDoc.isNull() && jsonDoc.isObject())
	{
    	QJsonObject jsonObj = jsonDoc.object();
    	for (auto key : jsonObj.keys()) 
      		dataMap[key] = jsonObj.value(key).toString();
   		
 	} 
 	else
    qDebug() << "Invalid JSON...";
    
	// 打印QMap内容
	for (auto key : dataMap.keys()) 
		qDebug() << key << ":" << dataMap[key];
}

说明

  • 从JSON字符串创建 QJsonDocument :使用QJsonDocument::fromJson 来解析JSON字符串。
  • 创建 QMap :定义一个 QMap<QString, QString> 来存储键值对。
  • 遍历JSON对象:使用 keys() 方法获取所有键,然后遍历这些键,将对应的值添加到 QMap 中。
  • 打印 QMap 内容:遍历 QMap 并打印键值对。

这个示例展示了如何将JSON对象的键值对解析到 QMap 中。这种方法适用于键值对类型的简单JSON对象。对于更复杂的JSON结构,可能需要更详细的解析逻辑。

五、软件开发网络通信架构

5.1 BS架构/CS架构

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

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

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

1. 客户端:

  • 用户界面通常在客户端呈现。
  • 可以是桌面应用程序、移动应用或专用软件
  • 负责向服务器发送请求,接收和处理服务器响应

2. 服务器:

  • 管理数据和业务逻辑
  • 处理来自客户端的请求,并发送回响应
  • 通常承载在远程系统上,如数据库服务器、应用服务器等

3. 特点:

  • 需要为每种操作系统或平台单独开发客户端
  • 高效的数据处理和响应能力
  • 在客户端设备上占用资源(如内存和处理能力)

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技术的能力。

在实际应用中,选择哪种架构取决于具体的业务需求、目标用户群、性能要求以及开发和维护的成本。

5.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)被用来在多个请求之间存储用户数据,从而为用户提供连贯的体验。

这些概念构成了HTTP的基础,是理解和使用HTTP协议的关键。每个概念都有它的具体细节和使用场景,了解这些有助于更好地在网络应用开发中应用HTTP。

5.3 Qt中的HTTP编程

  主要涉及使用Qt的网络模块来进行HTTP请求和处理HTTP响应。Qt提供了一系列类来处理网络通信,其中最常用的类是 QNetworkAccessManagerQNetworkRequestQNetworkReply 以及相关的支持类。
以下是一个基本的HTTP编程示例,展示了如何使用Qt发送一个简单的HTTP GET请求并处理响应:

步骤 1: 包含必要的头文件

#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QObject>
#include <QDebug>

步骤 2: 发送HTTP请求
  创建一个 QNetworkAccessManager 对象,并使用它发送HTTP请求。 QNetworkAccessManager 对象会异步地处理请求,并返回一个 QNetworkReply 对象。

int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);
	QNetworkAccessManager manager;
	QNetworkRequest request(QUrl("http://example.com"));
	//发起网络请求
	QNetworkReply *reply = manager.get(request);
	
	//绑定连接成功与槽函数
	QObject::connect(reply, &QNetworkReply::finished, [&]() {
		//应答异常
		if (reply->error()) {
			qDebug() << "Error:" << reply->errorString();
			return;
		}
		//读取网络返回的JSON数据,后续将着重做进一步的处理
		QString response = reply->readAll();
		qDebug() << "Response:" << response;
	});
	
	return a.exec();
}

  在这个例子中,我们使用 QNetworkAccessManager 的 get 方法发送了一个HTTP GET请求到特定网址。然后,我们连接了 QNetworkReply 对象的 finished 信号到一个lambda函数,该函数在收到HTTP响应时被调用。

注意事项

  1. 异步处理: QNetworkAccessManager 的请求是异步的。这意味着 get 方法会立即返回,而HTTP响应将在稍后通过信号处理。
  2. 错误处理: 应该检查 QNetworkReply 对象是否有错误,并相应地处理。
  3. 内存管理: QNetworkReply 对象需要被正确地管理,以避免内存泄漏。通常情况下,使用QObject::deleteLater 来安排删除它是一个好方法

6. 天气预报项目整合

6.1 连接请求解析JSON数据并刷新界面

  通过创建QNetworkAccessManager 进行发起网络请求,同时检测QNetworkAccessManager 的finished信号,表示接入网络完成,通过绑定finished与槽函数用于接收网络连接状态并接收来自网络的JSON数据。

1. 连接网络并绑定槽函数

//由 QNetworkAccessManager 发起get请求
manager = new QNetworkAccessManager(this);
//指定请求的url地址
strUrl = "http://v1.yiketianqi.com/api?unescape=1&version=v9&appid=89361488&appsecret=K6tTmCT0";
QUrl urlYiKeTianQi(strUrl);
QNetworkRequest request(urlYiKeTianQi);
//发起请求
manager->get(request);
//QNetworkReply *reply = manager->get(request);

//绑定连接请求成功槽函数
connect(manager, &QNetworkAccessManager::finished, this, &Widget::readHttReply);

2. 槽函数检测接入状态并接收数据
  槽函数带QNetworkReply 类的参数,通过attribute方法获取状态码,若访问数据成功则读取网页返回的JSON天气数据用于后续的处理,否则异常提示。

//读取HTTP网页请求的数据
void Widget::readHttReply(QNetworkReply *reply)
{
	//获取状态码
    int replyCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    //qDebug() << "replyCode: " << replyCode;

    //连接成功
    if(reply->error() == QNetworkReply::NoError && replyCode == 200)
    {
        //大多数服务器返回utf-8格式
        QByteArray data = reply->readAll();
        //应答成功,对接收到的数据进行JSON解析
        parseWeatherJsonData(data);
        //qDebug() << QString::fromUtf8(data);

    }
    else{
        qDebug() << "网络连接错误: " << reply->errorString();
        QMessageBox messageBox;
        messageBox.setWindowTitle("错误");
        messageBox.setText("网络连接错误");
        messageBox.setStyleSheet("QPushButton {color:black}");
        messageBox.setStandardButtons(QMessageBox::Ok);
        messageBox.exec();
    }
}

3. JSON数据解析
  通过封装函数用于单独的数据解析,将解析完成数据保存到一个类数组中,用于后续的界面刷新调取。

其中Day类的定义如下所示:

class Day
{
public:
    Day();
    QString mWeek;      //星期
    QString mDate;      //日期
    QString mCity;      //城市
    QString mTemp;      //温度
    QString mWeathType; //天气类型
    QString mTempLow;   //低温
    QString mTempHigh;  //高温

    QString mTips;      //提示
    QString mFx;        //风向
    QString mFl;        //风力
    QString mPm25;      //pm2.5值
    QString mHu;        //湿度
    QString mAirq;      //空气质量
};

定义类数组,用于保存接下去一周的天气数据

//定义类数组
Day days[6];

JSON数据解析函数具体实现如下所示:

//解析网页JSON 将数据保存至day类对象数组中 并调用刷新界面函数
void Widget::parseWeatherJsonData(QByteArray srcData)
{
    // 将JSON字符串转换为QJsonDocument
    QJsonDocument jsonDoc = QJsonDocument::fromJson(srcData);
    if (!jsonDoc.isNull() && jsonDoc.isObject())
    {
        // 将QJsonDocument 转化为 QJsonObject对象
        QJsonObject rootObj = jsonDoc.object();
        days[0].mCity = rootObj["city"].toString();
        days[0].mPm25 = rootObj["aqi"].toObject()["pm25"].toString();
        if( rootObj.contains("data") && rootObj["data"].isArray())
        {
            QJsonArray weaArray = rootObj["data"].toArray();
            //获取七天数据
            for(int i = 0; i < 6; i++)
            {

                QJsonObject dayObj = weaArray[i].toObject();
                days[i].mWeek = dayObj["week"].toString();
                days[i].mDate = dayObj["date"].toString();
                days[i].mWeathType = dayObj["wea"].toString();
                days[i].mAirq = dayObj["air_level"].toString();
                days[i].mFx = dayObj["win"].toArray()[0].toString();
                days[i].mFl = dayObj["win_speed"].toString();

                //温度
                days[i].mTemp = dayObj["tem"].toString();
                days[i].mTempLow = dayObj["tem2"].toString();
                days[i].mTempHigh = dayObj["tem1"].toString(); 
                //提示 + 湿度
                days[i].mTips = dayObj["index"].toArray()[3].toObject()["desc"].toString();
                days[i].mHu = dayObj["humidity"].toString();
            }
        }
        else
            qDebug() << "no array";
    }

    //刷新Widget04各标签数据
    uapateUI();
}

4. UI界面刷新数据

通过JSON解析出来的数据,将数据刷新到UI界面控件

//并刷新UI界面
void Widget::uapateUI()
{
    //Widget01 解析日期
    ui->labelDay->setText(days[0].mDate + " " + days[0].mWeek);

    //Widget02
    //解析气温 天气
    ui->labelWeatherPic->setPixmap(weatherPicMap[days[0].mWeathType]);
    ui->labelWeather->setText(days[0].mWeathType);
    ui->labelTempData->setText(days[0].mTemp+"℃");
    ui->labelTempRange->setText(days[0].mTempLow+ "℃ - " + days[0].mTempHigh + "℃");
    ui->labelCity->setText(days[0].mCity);

    //Widget03
    //感冒指数
    ui->labelGanMao->setText("提示:" + days[0].mTips);

    //Widget0301
    //解析风向、空气质量、湿度等
    ui->labelFengXiangType->setText(days[0].mFx);
    ui->labelFengXiangData->setText(days[0].mFl);
    ui->labelPM25Data->setText(days[0].mPm25);
    ui->labelShiDuData->setText(days[0].mHu);
    ui->labelAirData->setText(days[0].mAirq);

    //Widget04 label数据
    for(int i = 0; i < 6; i++)
    {
        //日期 + 时间
        weekList[i]->setText(days[i].mWeek);
        weekList[0]->setText("今天");
        weekList[1]->setText("明天");
        weekList[2]->setText("后天");
        QStringList dataList = days[i].mDate.split("-");    //2024-02-22
        dateList[i]->setText(dataList[1] + "-" + dataList[2]);

        //天气
        QPixmap pixmap = weatherPicMap[days[i].mWeathType];
        pixmap.scaled(pixmap.size(), Qt::KeepAspectRatio);
        weatherPicList[i]->setMaximumHeight(50);
        weatherPicList[i]->setMaximumWidth(ui->widget->width()/6);
        weatherPicList[i]->setPixmap(pixmap);
        weatherTypeList[i]->setText(days[i].mWeathType);

        //空气质量
        QString airQuality = days[i].mAirq;
        airQualityList[i]->setText(airQuality);

        //设置背景颜色
        if(airQuality == "优")
            airQualityList[i]->setStyleSheet("background-color: rgb(124, 198, 55); border-radius: 8px; color: rgb(230, 230, 230);");
        else if(airQuality == "良")
            airQualityList[i]->setStyleSheet("background-color: rgb(208, 107, 39); border-radius: 8px; color: rgb(230, 230, 230);");
        else if(airQuality == "轻度污染")
            airQualityList[i]->setStyleSheet("background-color: rgb(255, 200, 200); border-radius: 8px; color: rgb(230, 230, 230);");
        else if(airQuality == "中度污染")
            airQualityList[i]->setStyleSheet("background-color: rgb(255, 17, 17); border-radius: 8px; color: rgb(230, 230, 230);");
        else if(airQuality == "重度污染")
            airQualityList[i]->setStyleSheet("background-color: rgb(153, 0, 0); border-radius: 8px; color: rgb(230, 230, 230);");

        //风向 + 风力
        fengXiangList[i]->setText(days[i].mFx);
        fengLiList[i]->setText(days[i].mFl.split("转").at(0));
    }

    //更新绘图事件
    update();
}

6.2 支持不同城市查询的实现

  通过在网址后添加城市ID即可获取对应城市的天气JSON数据,因此核心在于根据行编辑器内的城市名称获取对应的城市id,而城市id和城市名称一一对应的关系数据存放在citycode.json文件中。

1. 读取JSON数据存放是QMap容器
  通过解析JSON数据,将各个城市的名称和id号以键值对的形式保存至QMap容器中,方式下次重复解析JSON数据

//将JSON数据读取到Map容器
void cityCodeUtils::initMap()
{
    // 读取JSON文件数据
    QFile file("E:/qtProject/00_QT_CLC/weather/citycode.json");
    if(!file.open(QIODevice::ReadOnly)){
        qDebug() << "---------getCityIdFromName file open error----------";
    }
    QString srcData = file.readAll();
    file.close();

    // 将JSON字符串转换为QJsonDocument
    QJsonDocument cityDoc = QJsonDocument::fromJson(srcData.toUtf8());
    if (!cityDoc.isNull() && cityDoc.isArray())
    {
        //各个城市对象
        QJsonArray cityArray = cityDoc.array();
        //遍历数组 遍历的每个为obj对象
        for(QJsonValue citys : cityArray)
        {
            if(citys.type() == QJsonValue::Object)
            {
                QString city_name = citys["city_name"].toString();
                QString city_code = citys["city_code"].toString();
                cityMap.insert(city_name,city_code);
            }
        }
    }
}

2. 通过城市名称获得城市id
  通过QMap中的find方法查找城市名称所对应的城市id,同时考虑市、区、县等多种形式的出现,若无该城市名称,则返回空。

//遍历容器 返回对应城市的城市ID
QString cityCodeUtils::getCityCode(QString cityName)
{
    if(cityMap.isEmpty())
        initMap();

    //查找cityName对应的迭代器,并返回值
    auto pos  = cityMap.find(cityName);
    if(pos == cityMap.end())
    {
        pos  = cityMap.find(cityName + "市");
        if(pos == cityMap.end())
        {
            pos  = cityMap.find(cityName + "区");
            if(pos == cityMap.end())
                pos  = cityMap.find(cityName + "县");
            if(pos == cityMap.end())
                return "";
        }
    }
    return pos .value();
}

3. 搜索按钮槽函数发起新地址访问
  在搜索按钮的槽函数中调用获取城市id的函数,通过获取文本框内的城市名称得到相应的城市id,并将id号拼接至url后重新发起新的网页请求。

//搜索槽函数 点击进行重新发起请求
void Widget::on_btnSearch_clicked()
{
    QString cityNameInput = ui->lineEditSearch->text();
    cityId = cityUtils.getCityCode(cityNameInput);
    if(cityId != NULL)
    {
        //更新带城市信息的网址
        strUrl = "http://v1.yiketianqi.com/api?unescape=1&version=v9&appid=89361488&appsecret=K6tTmCT0";
        //拼接城市号,生成新的请求网址
        strUrl += "&cityid=" + cityId;
        //发起请求
        manager->get(QNetworkRequest(QUrl(strUrl)));
    }
    else	//若无该城市,则弹窗提示
    {
        QMessageBox messageBox;
        messageBox.setWindowTitle("错误");
        messageBox.setText("请出入正确的城市名称");
        messageBox.move(this->pos().x() + 200, this->pos().y() + 500);
        messageBox.setStyleSheet("QPushButton {color:black}");
        messageBox.setStandardButtons(QMessageBox::Ok);
        messageBox.exec();
    }
}

6.3 七日温度曲线的绘制

   因为需要在特定的widget0404和0405上绘制温度曲线,而不是在整个大截面绘制温度曲线,因此需在构造函数中对widget0404和05两个控制安装事件过滤器器

ui->widget0404->installEventFilter(this);
ui->widget0405->installEventFilter(this);

   在事件过滤函数中检测事件发生对象是否为该两个对象,同时是否为画家事件,若成立则进行温度曲线的绘制,若否则返回QWidget的原始事件

//事件过滤器 过滤widget0404 widget0405有绘图事件时,刷新绘图界面
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    //widget0404 绘图事件
    if(watched == ui->widget0404 && event->type() == QEvent::Paint)
    {
        drawTempLineHight();    //绘制高温曲线图
        return true;            //表示事件已被处理
    }
    //widget0405 绘图事件
    if(watched == ui->widget0405 && event->type() == QEvent::Paint)
    {
        drawTempLineLow();  //绘制低温曲线图
        return true;        //表示事件已被处理
    }
    //返回原始事件
    return QWidget::eventFilter(watched, event);
}

   在widget中间绘制不同天气的温度点、线和文字,其中点的横坐标为控件的中间位置,纵坐标为控件的中间位置与温度和平均位置的差。

//绘制widget0404高温曲线
void Widget::drawTempLineHight()
{
    QPainter painter(ui->widget0404);

    //消除边缘混叠
    painter.setRenderHint(QPainter::Antialiasing,true);

    //设置画笔与画刷
    painter.setBrush(QColor(255, 170, 0 ));
    painter.setPen(QColor(255, 170, 0));

    //定义6个点
    QPoint points[6];

    //计算高温的平均值
    float ave;
    int sum = 0;
    for(int i = 0; i < 6; i++)
    {
        sum += days[i].mTempHigh.toInt();

    }
    ave = sum / 6.0;
    //qDebug() << "ave: " << ave;

    //绘图
    for(int i = 0; i < 6; i++)
    {
        //画点
        points[i].setX(airQualityList[i]->x() + airQualityList[i]->width()/2);
        points[i].setY(ui->widget0404->height()/2 - (days[i].mTempHigh.toInt() - ave) * 3);
        painter.drawEllipse(QPoint(points[i].x(), points[i].y()),3,3);

        //绘制温度
        painter.drawText(QPoint(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]);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/410574.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vi/Vim 使用小窍门,如何消除搜索后的关键字高亮

Vim/Vi 基本上是 *nix 世界最受欢迎的编辑器了&#xff0c;不知道为什么&#xff0c;一直以来觉得和 Emacs 比起来&#xff0c;Vim 更加有亲和力。用起来很舒服。 今天就记录一个困扰了我很久的问题。 大家应该都知道&#xff0c;在 Vi 里面如果要搜索某个关键字&#xff0c;…

2024国际生物发酵展览会不容错过-欧瑞安电气

参展企业介绍 山东欧瑞安电气有限公司成立于2013年&#xff0c;坐落于泰山脚下的泰安国家高新区&#xff0c;是国家高新技术企业、国家专精特新“小巨人”企业、中国产学研合作创新示范企业、山东省“隐形冠军”企业、山东省技术创新示范企业、山东省高端品牌培育企业、山东省…

Mac使用K6工具压测WebSocket

commend空格 打开终端&#xff0c;安装k6 brew install k6验证是否安装成功 k6 version设置日志级别为debug export K6_LOG_LEVELdebug执行脚本&#xff08;进入脚本所在文件夹下&#xff09; k6 run --vus 100 --duration 10m --out csvresult.csv script.js 脚本解释&…

免费分享一套SpringBoot+Vue实验室(预约)管理系统,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue实验室(预约)管理系统 &#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue实验室(预约)管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue实验室(预约)管理系统 Java毕…

【深度学习笔记】3_11 模型选择、欠拟合和过拟合

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;做了部分个人理解标注&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 3.11 模型选择、欠拟合和过拟合 在前几节基于Fashion-MNIST数据集的实验中&#xff0c;我们评价了机器学习模型在训练数据集和测试数…

【前端】nginx 反向代理,实现跨域问题

前面讲跨域的问题&#xff0c;这篇 C# webapi 文章里面已经说过了。在上述文章中是属于从服务器端去允许访问的策略去解决跨域问题。而这里是从客户端的角度利用反向代理的方法去解决跨域问题。 反向代理&#xff1a;其原理就是将请求都接收到一个中间件&#xff08;中间地址&a…

【SRE系列之Jenkins的使用】--实现ssh和http克隆

1、Jenkins的概念 1.1Jenkins的介绍 Jenkins是一个独立的开源软件项目&#xff0c;是基于Java开发的一种CI&#xff08;Continuous integration&#xff0c;持续集成&#xff09; &CD (Continuous Delivery&#xff0c;持续交付)工具&#xff0c;用于监控持续重复的工作&a…

深入浅出JVM(十二)之垃圾回收算法

上篇文章深入浅出JVM&#xff08;十一&#xff09;之如何判断对象“已死”已经深入浅出的解析JVM是如何评判对象不再使用&#xff0c;不再使用的对象将变成“垃圾”&#xff0c;等待回收 垃圾回收算法有多种&#xff0c;适用于不同的场景&#xff0c;不同的垃圾收集器使用不同…

Matlab自学笔记二十七:详解格式化文本sprintf各参数设置方法

1.一个程序引例 上篇文章已经介绍了格式化文本的初步应用&#xff0c;程序示例如下&#xff1a; sprintf(|%f\n|%.2f\n|%8.2f,pi*ones(1,3)) 2.格式化操作符各字段的含义解析 格式化操作符可以有六个字段&#xff0c;只有主字符%和转换格式是必需的&#xff0c;其他都是可选…

DWT硬件延时

DWT硬件延时 文章目录 DWT硬件延时软件&硬件延时方案软件延时硬件延时方案 DWT硬件延时方案DWT硬件延时方案DEMCR寄存器DWT硬件延时方案实现延时初始化&#xff1a;US延时&#xff1a;MS延时&#xff1a; 软件&硬件延时方案 软件延时 static void Delay(uint32_t cou…

Sora抢饭碗!好莱坞大亨停止8亿美元投资

好莱坞消息&#xff0c;著名演员、影视投资人Tyler Perry在看到OpenAI最新发布的文生视频模型Sora后&#xff0c;停止了8亿&#xff08;约57亿元&#xff09;美元的投资。 该投资项目位于亚特兰大&#xff0c;本来要扩展十几个摄影棚用于影视剧的拍摄&#xff08;类似横店影视…

企业微信主体怎么转让给别人?

企业微信变更主体有什么作用&#xff1f;当我们的企业因为各种原因需要注销或已经注销&#xff0c;或者运营变更等情况&#xff0c;企业微信无法继续使用原主体继续使用时&#xff0c;可以申请企业主体变更&#xff0c;变更为新的主体。企业微信变更主体的条件有哪些&#xff1…

C++笔记(面对对象部分复习向)

B站&#xff1a;黑马程序员C教程 栈区&#xff0c;全局区&#xff0c;堆区和代码区 析构、构造和static 对象成员与类本身构造顺序&#xff0c;先成员后自己&#xff1b;析构则相反 static修饰成员变量,所有对象共享一份内存&#xff0c;编译阶段分配内存&#xff0c;类内声明…

Windows部署WebDAV服务并映射到本地盘符实现公网访问本地存储文件

文章目录 前言1. 安装IIS必要WebDav组件2. 客户端测试3. 使用cpolar内网穿透&#xff0c;将WebDav服务暴露在公网3.1 安装cpolar内网穿透3.2 配置WebDav公网访问地址 4. 映射本地盘符访问 前言 在Windows上如何搭建WebDav&#xff0c;并且结合cpolar的内网穿透工具实现在公网访…

使用CE查找共享代码的多种方法

一般在游戏中,我们会有这样的定义Player和怪物NPC: // 基类 Character class Character { protected:std::string name;int64_t id;int32_t hp;int32_t mp;int32_t level;public:Character(const int64_t name) : id(id) {}virtual void attack() {…

做了个很牛的网站,可以搜索网站的网站到底有多好用?

今天给大家推荐的网站叫做&#xff1a;毒蘑菇 - 搜索 毒蘑菇搜索&#xff0c;顾名思义呢&#xff0c;搜索的功能比较好用&#xff0c;大家上网的时候总是需要记住网站的地址&#xff0c;即使你知道网站的名称&#xff0c;也得跳转到百度然后在搜索&#xff0c;有时候百度上那么…

Windows Server 2012 IIS中发布ASP.NET CORE项目

服务器安装IIS&#xff1a; 微软官网下载SDK&#xff1a; 下载Runtime官网&#xff1a;https://dotnet.microsoft.com/download/dotnet-core 安装成功重启IIS&#xff1a; VS发布项目&#xff1a;

流浪动物救助平台:Java开发的实践与思考

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

DevOps 周期的 6 个 C

中型到大型软件开发项目涉及许多人员、多个团队、资源、工具和开发阶段。它们都需要以某种方式进行管理和简化&#xff0c;不仅可以获得所需的产品&#xff0c;而且还要确保将来在不断变化的环境下易于管理和维护。组织通常遵循许多项目管理模型和技术。DevOps 是其中之一&…

精益生产,创新驱动:机器人技术引领企业的未来之路

随着自动化技术的普及和物联网、人工智能技术的迅速发展&#xff0c;全球制造业正在经历着一场革命性的变革。传统以人力劳动为基础的制造业正逐渐向以机器为主导的智能生产模式转型。企业们正积极探索更高效的制造方式&#xff0c;通过新兴技术提升生产力&#xff0c;同时降低…