【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();
}
说明
- 创建JSON对象:使用 QJsonObject 来构建JSON对象,并使用键值对填充数据。
- 创建JSON数组:使用 QJsonArray 来创建一个数组,并添加元素。
- 组合JSON结构:将JSON数组添加到JSON对象中。
- 生成JSON文档:通过 QJsonDocument 来处理JSON数据,可以选择格式化(缩进)或压缩形式。
- 保存到文件:创建 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数组,其中的元素是对象时,使用 QJsonArray
和 QJsonObject
来创建和处理这些数据结构。以上就是一个示例,展示了如何创建一个包含多个对象的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字符串转换为QJsonDocumentQJsonDocument 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字符串转换为QJsonDocumentQJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8());// 准备一个QMap来存储解析的数据QMap<QString, QString> dataMap;// 解析JSON对象并填充QMapif (!jsonDoc.isNull() && jsonDoc.isObject()){QJsonObject jsonObj = jsonDoc.object();for (auto key : jsonObj.keys()) dataMap[key] = jsonObj.value(key).toString();} elseqDebug() << "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提供了一系列类来处理网络通信,其中最常用的类是 QNetworkAccessManager
、 QNetworkRequest
、 QNetworkReply
以及相关的支持类。
以下是一个基本的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响应时被调用。
注意事项
- 异步处理:
QNetworkAccessManager
的请求是异步的。这意味着 get 方法会立即返回,而HTTP响应将在稍后通过信号处理。 - 错误处理: 应该检查
QNetworkReply
对象是否有错误,并相应地处理。 - 内存管理:
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字符串转换为QJsonDocumentQJsonDocument 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();}}elseqDebug() << "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-22dateList[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字符串转换为QJsonDocumentQJsonDocument 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]);
}