主页 > 电脑硬件  > 

Qt之天气预报——功能实现篇(含源码+注释)

Qt之天气预报——功能实现篇(含源码+注释)

文章目录 一、功能概述1.基本功能2.实时天气模式3.预报天气模式 二、天气预报功能示例图1.城市选择(下拉框)2.城市选择(文本框)3. 预报天气日期切换4.刷新操作 三、使用类的简述3.1 涉及的Qt类3.2 自定义类3.2.1 自定义结构体3.2.2 自定义类 四、配置文件1.API链接配置文件——WeatherApi.xml1.城市编码配置文件——CityCodeInfo.xml 五、遇到的问题1.图表坐标轴不显示2.时间轴数据索引位置对应 六、源码6.1 结构体头文件6.2 预报天气按钮类6.3 预报天气模块类6.4 天气预报主类 总结相关文章天气预报系列相关类的基本使用

一、功能概述

天气预报包含实时天气模式和预报天气模式。

1.基本功能

添加右键菜单; 可切换天气模式; 显示天气报告时间; 刷新功能(右键菜单); 城市选择模式:包括下拉框选择和文本框输入(右键菜单); 切换城市更新天气预报信息,显示报告时间、城市、温度等常用信息。

2.实时天气模式

实时天气模式功能比较单一,仅显示当天的天气基本信息。

3.预报天气模式

预报天气包含四天的天气信息(包括当天天气),默认显示当天天气信息;可通过自定义按钮对象切换天气信息,且预报天气下方为预报日期日、夜间温度的折线图。

二、天气预报功能示例图 1.城市选择(下拉框)

下图通过下拉列表框切换城市从而自动查询天气,其中包括实时天气和预报天气。

2.城市选择(文本框)

下图演示文本框的联想功能,以及通过城市编码获取天气操作。

3. 预报天气日期切换

下图通过预报天气中的星期模块切换对应日期的信息。

4.刷新操作

下图通过右键菜单刷新按钮重新获取天气信息,肉眼可见图表的刷新。 提示:不会使用Qt设计师设计界面的小伙伴点击这里

三、使用类的简述 3.1 涉及的Qt类

QFile:读取配置文件。 QDateTime:转换时间串。 QCompleter:文本框联想类。 QMessageBox:提示对话框。 QDomDocument、QDomElement:解析XML配置文件;须在pro文件添加“QT += xml”。 QJsonDocument、QJsonObject、QJsonArray:解析天气预报返回的Json串。 QChartView、QChart、QLineSeries、QDateTimeAxis、QValueAxis:绘制折线图;须在pro文件添加“QT += charts”,并在图表使用类中添加“QT_CHARTS_USE_NAMESPACE”使用命名空间。 QNetworkAccessManager、QNetworkReply、QNetworkRequest:天气预报信息申请和获取返回的Json串。 注:简述仅代表类在本文中的作用。

3.2 自定义类 3.2.1 自定义结构体

stApiInfo:存储API链接信息结构体;可以生成链接、设置链接和判断链接信息释放包含空。 stLiveInfo:存储实时信息结构体;可设置实时信息和判断实时信息是否包含空。 stForecastsInfo:存储预报信息结构体;可设置单个预报天气信息内容。 stWeatherInfo:存储天气信息结构体;包含基础信息、实时信息结构体和预报信息结构体链表。

3.2.2 自定义类

CWeatherForecast:天气预报类;其中包含API信息结构体、天气信息结构体、城市信息等。 CForecastWidget:预报天气信息显示类;主要显示预报天气信息和切换预报天气。 CForecastBtn:预报天气天气切换按钮类;响应鼠标点击发送索引值,从而切换天气,原型为QWidget,但是主要响应点击信号,所以在此称之为按钮。

四、配置文件 1.API链接配置文件——WeatherApi.xml

配置文件内容可自定义,API_KEY的申请详情可查看Qt之天气预报实现(一)预备篇。

<?xml version="1.0" encoding="UTF-8"?> <Root Type = "ApiInfo"> <Link Link = "http://restapi.amap /v3/weather/weatherInfo?"/> <ApiKey ApiKey = "自己申请的API KEy"/> <CityCode CityCode = "110000"/> <Extensions Extensions = "base"/> </Root> 1.城市编码配置文件——CityCodeInfo.xml

城市编码可通过Web服务 API 相关下载下载。 注:因城市编码信息过多,在此仅展示部分。

<?xml version="1.0" encoding="UTF-8"?> <Root Type = "CityInfo"> <CityInfo Name = "北京市" Code = "110000" /> <CityInfo Name = "北京市市辖区" Code = "110100" /> <CityInfo Name = "东城区" Code = "110101" /> <CityInfo Name = "西城区" Code = "110102" /> ...... <CityInfo Name = "风顺堂区" Code = "820005" /> <CityInfo Name = "嘉模堂区" Code = "820006" /> <CityInfo Name = "路凼填海区" Code = "820007" /> <CityInfo Name = "圣方济各堂区" Code = "820008" /> </Root> 五、遇到的问题 1.图表坐标轴不显示

通过序列对象的attachAxis()函数将序列对象与X/Y坐标轴对象关联起来即可。

2.时间轴数据索引位置对应

时间轴起始值为1970/01/01,当我设置时间轴范围时,需要将获取日期时间值的毫秒值,然后将毫秒值和温度值添加到序列中即可(详情请见源码)。

六、源码 6.1 结构体头文件

StWeatherForecast.h

#ifndef STWEATHERFROECAST_H #define STWEATHERFROECAST_H #include <QString> #include <QHash> #include <QDate> // 定义api信息结构体 typedef struct stApiInfo { QString link; // 链接 QString apiKey; // API Key QString cityCode; // 城市编码 QString extensions; // 天气查看类型 // api信息结构体初始化 stApiInfo() { link = "http://restapi.amap /v3/weather/weatherInfo?"; // 链接 apiKey = "自己的APIKEY"; // apiKey cityCode = "110000"; // 城市码 extensions = "base"; // 天气查看类型(实时、预报) } /** * @brief joinApiLink 拼接API链接 * @return 返回拼接好的API链接 */ QString joinApiLink() { QString ret = ""; // 拼接API_KEY ret = link + "key=" + apiKey; ret += "&city=" + cityCode; ret += "&extensions=" + extensions; return ret; } /** * @brief fieldIsEmpty 判断是否有字段值为空 * @return 是否为空 */ bool fieldIsEmpty() { return link.isEmpty() || apiKey.isEmpty() || cityCode.isEmpty() || extensions.isEmpty(); } }stApiInfo; // 实时天气信息结构体 typedef struct stLiveInfo { QString weather; // 天气 QString temperature; // 温度 QString winddirection; // 风向 QString windpower; // 风力等级 QString humidity; // 湿度 // 无参构造 stLiveInfo() { } // 使用构造函数使其将数据传入,通过初始化列表赋值 stLiveInfo(const QString &weather, const QString &temperature , const QString &winddirection, const QString &windpower, const QString &humidity) : weather(weather) , temperature(temperature) , winddirection(winddirection) , windpower(windpower) , humidity(humidity) { } /** * @brief setWeatherInfo 设置天气信息 * @param weather 天气 * @param temperature 温度 * @param winddirection 风向 * @param windpower 风力等级 * @param humidity 湿度 */ void setWeatherInfo(const QString &weather, const QString &temperature , const QString &winddirection, const QString &windpower, const QString &humidity) { this->weather = weather; this->temperature = temperature; this->winddirection = winddirection; this->windpower = windpower; this->humidity = humidity; } /** * @brief fieldIsEmpty 判断是否有字段值为空 * @return 是否为空 */ bool fieldIsEmpty() { return weather.isEmpty() || temperature.isEmpty() || winddirection.isEmpty() || windpower.isEmpty() || humidity.isEmpty(); } }stLiveInfo; // 预报天气信息结构体 typedef struct stForecastsInfo { private: QHash<int, QString> weekHash; public: QString date; // 日期 QString week; // 星期 QString dayweather; // 日间天气 QString nightweather; // 夜间天气 QString daytemp; // 日间温度 QString nighttemp; // 夜间温度 QString daywind; // 日间风向 QString nightwind; // 夜间风向 QString daypower; // 日间风力等级 QString nightpower; // 夜间风力等级 // 创建无参构造 stForecastsInfo() { //! 初始化Hash的值,为方便获取周数据 //! 此次可通过配置文件读取,不过我在这里就偷一下懒了,hhh weekHash[1] = "周一"; weekHash[2] = "周二"; weekHash[3] = "周三"; weekHash[4] = "周四"; weekHash[5] = "周五"; weekHash[6] = "周六"; weekHash[7] = "周日"; } /** * @brief setDate 设置日期(自动设置星期) * @param dateStr 日期字符串 */ void setDate(const QString &dateStr) { // 结构体日期变量赋值 this->date = dateStr; // 将日期字符串转为QDate对象(必须设置字符串的日期格式哦,否则识别不到日期) QDate date = QDate::fromString(dateStr, "yyyy-MM-dd"); // 结构体星期变量赋值 this->week = weekHash[date.dayOfWeek()]; } /** * @brief setWeather 设置日/夜间天气 * @param dayweather 日间天气 * @param nightweather 夜间天气 */ void setWeather(const QString &dayweather, const QString &nightweather) { this->dayweather = dayweather; this->nightweather = nightweather; } /** * @brief setTemperature 设置日/夜间温度 * @param daytemp 日间温度 * @param nighttemp 夜间温度 */ void setTemperature(const QString &daytemp, const QString &nighttemp) { this->daytemp = daytemp; this->nighttemp = nighttemp; } /** * @brief setWindDirection 设置日/夜间风向 * @param daywind 日间风向 * @param nightwind 夜间风向 */ void setWindDirection(const QString &daywind, const QString &nightwind) { this->daywind = daywind; this->nightwind = nightwind; } /** * @brief setWindPower 设置日/夜间风力等级 * @param daypower 日间风力等级 * @param nightpower 夜间风力等级 */ void setWindPower(const QString &daypower, const QString &nightpower) { this->daypower = daypower; this->nightpower = nightpower; } }stForecastsInfo; // 天气信息结构体 typedef struct stWeatherInfo { // 基本信息(实时、预报天气都有以下属性) QString province; // 省份 QString city; // 城市 QString adcode; // 区域编码 QString reporttime; // 报告时间 // 因为实时信息和预报信息内容不同,需分开存储 stLiveInfo stliveInfo; // 实时天气信息 // 实时信息包含多天天气,需用容器存储 QList<stForecastsInfo> stForecastInfoList; // 预报天气信息 /** * @brief setBaseInfo 设置基础信息函数 * @param province 省份 * @param city 成都 * @param adcode 区域编码 * @param reportTime 报告时间 */ void setBaseInfo(const QString &province, const QString &city , const QString &adcode, const QString &reportTime) { this->province = province; this->city = city; this->adcode = adcode; this->reporttime = reportTime; } }stWeatherInfo; #endif // STWEATHERFROECAST_H 6.2 预报天气按钮类

注:因其功能主要为点击,所以称为按钮类。 CForecastBtn.h

#ifndef CFORECASTBTN_H #define CFORECASTBTN_H #include <QWidget> namespace Ui { class CForecastBtn; } class CForecastBtn : public QWidget { Q_OBJECT public: explicit CForecastBtn(QWidget *parent = nullptr); ~CForecastBtn(); /** * @brief setBtnInfo 设置按钮显示信息 * @param week 周几 * @param weather 天气 * @param dayTemp 日间温度 * @param nightTemp 夜间温度 */ void setBtnInfo(QString week, QString weather, QString dayTemp, QString nightTemp); signals: /** * @brief clicked 点击信号 */ void clicked(); private: Ui::CForecastBtn *ui; // QWidget interface protected: /** * @brief mouseReleaseEvent 鼠标释放时间 * @param event 事件对象 */ void mouseReleaseEvent(QMouseEvent *event); }; #endif // CFORECASTBTN_H

CForecastBtn.cpp

#include "CForecastBtn.h" #include "ui_CForecastBtn.h" #include <QMouseEvent> CForecastBtn::CForecastBtn(QWidget *parent) : QWidget(parent), ui(new Ui::CForecastBtn) { ui->setupUi(this); } CForecastBtn::~CForecastBtn() { delete ui; } void CForecastBtn::setBtnInfo(QString week, QString weather, QString dayTemp, QString nightTemp) { // 设置周标签 ui->weekLab->setText(week); // 设置天气 ui->weatherLab->setText(weather); // 设置温度标签 ui->tempLab->setText(nightTemp + "°~" + dayTemp + "°"); } void CForecastBtn::mouseReleaseEvent(QMouseEvent *event) { // 当前为左键按钮时进入 if(Qt::LeftButton == event->button()) { // 发出点击信号 emit clicked(); } }

CForecastBtn.ui

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>CForecastBtn</class> <widget class="QWidget" name="CForecastBtn"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>72</width> <height>66</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item alignment="Qt::AlignHCenter"> <widget class="QLabel" name="weekLab"> <property name="text"> <string>星期</string> </property> </widget> </item> <item alignment="Qt::AlignHCenter"> <widget class="QLabel" name="weatherLab"> <property name="text"> <string>天气</string> </property> </widget> </item> <item alignment="Qt::AlignHCenter"> <widget class="QLabel" name="tempLab"> <property name="text"> <string>0°C~0°C</string> </property> </widget> </item> </layout> </widget> <resources/> <connections/> </ui> 6.3 预报天气模块类

CForecastWidget.h

#ifndef CFORECASTWIDGET_H #define CFORECASTWIDGET_H #include "StWeatherForecast.h" #include <QWidget> #include <QtCharts/QChart> #include <QtCharts/QLineSeries> #include <QValueAxis> #include <QDateTimeAxis> QT_CHARTS_USE_NAMESPACE // 使用chart的命名空间 namespace Ui { class CForecastWidget; } class CForecastWidget : public QWidget { Q_OBJECT public: explicit CForecastWidget(QWidget *parent = nullptr); ~CForecastWidget(); /** * @brief initialize 初始化类(使构造函数不做过多的操作) * @return 初始化标志值(通过该值区分是否初始化成功或初始化到哪一步) */ int initialize(); /** * @brief unInitialize 初始化类(使析构函数不做过多的操作) * @return 反初始化标志值(通过该值区分是否释放成功或释放到哪一步) */ int unInitialize(); /** * @brief updateWeatherInfo 更新按钮和图表信息 * @param infoList 预报天气结构体联邦 */ void updateWeatherInfo(const QList<stForecastsInfo> &infoList); /** * @brief releaseBtn 释放自定义按钮对象 */ void releaseBtn(); signals: /** * @brief forecastBtnClickedSig 转发按钮点击信号 * @param index 点击按钮索引 */ void forecastBtnClickedSig(int index); public slots: private: Ui::CForecastWidget *ui; QChart *m_lineChart; // 折线图的chart QLineSeries *m_daySeries; // 日间温度序列 QLineSeries *m_nightSeries; // 夜间温度序列 QDateTimeAxis *m_dateXAxis; // 折线图的X轴 QValueAxis *m_valueYAxis; // Y轴对象 }; #endif // CFORECASTWIDGET_H

CForecastWidget.cpp

#include "CForecastWidget.h" #include "ui_CForecastWidget.h" #include "CForecastBtn.h" #include <QtCharts/QChartView> CForecastWidget::CForecastWidget(QWidget *parent) : QWidget(parent) , ui(nullptr) , m_lineChart(nullptr) , m_daySeries(nullptr) , m_nightSeries(nullptr) , m_dateXAxis(nullptr) , m_valueYAxis(nullptr) { } CForecastWidget::~CForecastWidget() { } int CForecastWidget::initialize() { // 返回值对象 int ret = 0; if(nullptr == ui) { ui = new Ui::CForecastWidget; ui->setupUi(this); ret |= 0x1; } // 创建qchart对象 if(nullptr == m_lineChart) { // 创建图表对象 m_lineChart = new QChart; // 将图例对象放到图表的左边 m_lineChart->legend()->setAlignment(Qt::AlignLeft); // 创建默认坐标轴 m_lineChart->createDefaultAxes(); ret |= 0x10; } // 创建日间序列对象 if(nullptr == m_daySeries) { m_daySeries = new QLineSeries; // 设置序列名 m_daySeries->setName("日间温度"); ret |= 0x100; } // 创建夜间序列对象 if(nullptr == m_nightSeries) { m_nightSeries = new QLineSeries; // 设置序列名 m_nightSeries->setName("夜间温度"); ret |= 0x1000; } // 创建时间(X)轴对象 if(nullptr == m_dateXAxis) { m_dateXAxis = new QDateTimeAxis; // 设置坐标轴标签显示格式 m_dateXAxis->setFormat("yyyy/MM/dd"); ret |= 0x10000; } // 创建Y轴对象 if(nullptr == m_valueYAxis) { m_valueYAxis = new QValueAxis; ret |= 0x100000; } // 通过返回值对象判断所有对象是否初始化完成,然后做关联设置操作 if(0x111111 == ret) { // 将序列对象添加到chart对象中并将chart对象添加到ui中的GraphiView对象中 m_lineChart->addSeries(m_daySeries); m_lineChart->addSeries(m_nightSeries); // 添加X、Y轴对象到图表中 m_lineChart->addAxis(m_dateXAxis, Qt::AlignBottom); m_lineChart->addAxis(m_valueYAxis, Qt::AlignLeft); //! 序列对象关联X、Y坐标轴 // 关联X轴 m_daySeries->attachAxis(m_dateXAxis); m_nightSeries->attachAxis(m_dateXAxis); // 关联Y轴 m_daySeries->attachAxis(m_valueYAxis); m_nightSeries->attachAxis(m_valueYAxis); // 将图表对象添加到ui中 ui->weatherChartView->setChart(m_lineChart); } return ret; } int CForecastWidget::unInitialize() { // 返回值对象 int ret = 0; // 释放自定义按钮对象 releaseBtn(); // 释放Y轴 if(nullptr != m_valueYAxis) { delete m_valueYAxis; m_valueYAxis = nullptr; ret |= 0x1; } // 释放X轴 if(nullptr != m_dateXAxis) { delete m_dateXAxis; m_dateXAxis = nullptr; ret |= 0x10; } // 释放夜间序列对象 if(nullptr != m_nightSeries) { delete m_nightSeries; m_nightSeries = nullptr; ret |= 0x100; } // 释放日间序列对象 if(nullptr != m_daySeries) { delete m_daySeries; m_daySeries = nullptr; ret |= 0x1000; } // 释放qchart对象 if(nullptr != m_lineChart) { delete m_lineChart; m_lineChart = nullptr; ret |= 0x10000; } // 释放Ui对象 if(nullptr != ui) { delete ui; ui = nullptr; ret |= 0x100000; } return ret; } void CForecastWidget::updateWeatherInfo(const QList<stForecastsInfo> &infoList) { // 释放前一次设置的按钮对象 releaseBtn(); // 清空日间、夜间序列内容 m_daySeries->clear(); m_nightSeries->clear(); // 获取温度最值,方便设置图表Y轴的最值 int maxTemp = infoList.first().daytemp.toInt(); // 从日间温度取最大值 int minTemp = infoList.first().nighttemp.toInt(); //从夜间温度取最小值 // 设置时间坐标轴日期范围 m_dateXAxis->setRange(QDateTime::fromString(infoList.first().date, "yyyy-MM-d") , QDateTime::fromString(infoList.last().date, "yyyy-MM-dd")); // 遍历预报天气结构体信息容器 for(int index = 0; index != infoList.size(); ++index) { // 获取当前日期信息对象 const stForecastsInfo &info = infoList.at(index); // 创建按钮对象 CForecastBtn *btn = new CForecastBtn; // 将按钮添加到布局器中 ui->forecastBtnLayout->addWidget(btn); // 设置按钮对按钮对象的显示信息 btn->setBtnInfo(info.week, info.dayweather, info.daytemp, info.nighttemp); // 连接按钮信号信号槽 connect(btn, &CForecastBtn::clicked, [=]() { // 转发点击信号 emit forecastBtnClickedSig(index); }); // 获取日/夜间温度 int dayTemp = info.daytemp.toInt(); int nightTemp = info.nighttemp.toInt(); //! 时间轴对象需要将时间转换为毫秒来定位索引 qint64 timeIndex = QDateTime::fromString(infoList[index].date, "yyyy-MM-d").toMSecsSinceEpoch(); // 将日/夜间温度追加到对应的序列对象中 m_daySeries->append(timeIndex, dayTemp); m_nightSeries->append(timeIndex, nightTemp); // 通过三目运算符比较当前日/夜间温度,并取出较大/小值 maxTemp = dayTemp > maxTemp ? dayTemp : maxTemp; minTemp = nightTemp < minTemp ? nightTemp : minTemp; } // 设置Y轴的值范围, 并在最大/小值加/减2使图像不那么靠近边界 m_valueYAxis->setRange(minTemp - 2, maxTemp + 2); } void CForecastWidget::releaseBtn() { // 定义布局器item对象 QLayoutItem *child; // 循环获取布局器item对象,并判其不为空 while(nullptr != (child = ui->forecastBtnLayout->takeAt(0))) { // 获取item对象中的widget QWidget *wgt = child->widget(); // 判断widget是否为空 if(nullptr != wgt) { // 释放widget空间 delete wgt; } // 释放widget空间 delete child; } }

CForecastWidget.ui

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>CForecastWidget</class> <widget class="QWidget" name="CForecastWidget"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1"> <property name="spacing"> <number>0</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="forecastBtnLayout"/> </item> <item> <widget class="QChartView" name="weatherChartView"/> </item> </layout> </widget> <customwidgets> <customwidget> <class>QChartView</class> <extends>QGraphicsView</extends> <header location="global">qchartview.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui> 6.4 天气预报主类

CWeatherForecast.h

#ifndef CWEATHERFORECAST_H #define CWEATHERFORECAST_H #include "StWeatherForecast.h" #include "CForecastWidget.h" #include <QMainWindow> #include <QDomElement> #include <QNetworkAccessManager> // 导入请求/接受天气查询API的类 #include <QCompleter> namespace Ui { class CWeatherForecast; } class CWeatherForecast : public QMainWindow { Q_OBJECT public: explicit CWeatherForecast(QWidget *parent = nullptr); ~CWeatherForecast(); /** * @brief initialize 初始化类(使构造函数不做过多的操作) * @return 初始化标志值(通过该值区分是否初始化成功或初始化到哪一步) */ int initialize(); /** * @brief unInitialize 初始化类(使析构函数不做过多的操作) * @return 反初始化标志值(通过该值区分是否释放成功或释放到哪一步) */ int unInitialize(); /** * @brief loadConfig 加载配置文件 * @param path 指定文件路径 * @return 是否加载成功 */ bool loadConfig(QString path); /** * @brief loadWeatherForecastCfg 加载天气预报配置文件 * @param path 文件路径 * @return 是否加载成功 */ bool loadWeatherForecastCfg(QString path); /** * @brief readCityInfo 读取城市信息 * @param root root节点 * @return 是否读取成功 */ bool readCityInfo(const QDomElement &root); /** * @brief readAPiInfo 读取API信息 * @param root root节点 * @return 是否读取成功 */ bool readAPiInfo(const QDomElement &root); private: /** * @brief parseJson json语句转换槽函数 * @param jsonArray json语句容器 */ int parseJson(QByteArray jsonData); /** * @brief parseLiveJson 解析实时天气信息 * @param weatherInfo 实时天气信息对象 */ void parseLiveJson(QJsonObject weatherInfo); /** * @brief parseForecastJson 解析预报天气信息 * @param weatherInfo 预报天气信息对象 */ void parseForecastJson(QJsonObject weatherInfo); /** * @brief loadUiInfo 根据标记值加载ui * @param isChecked 标记值 */ void loadUiInfo(bool isChecked); /** * @brief loadForeCastInfo 加载指定预报信息结构体的内容 * @param info 信息结构体 */ void loadForeCastInfo(const stForecastsInfo &info); /** * @brief judgeCityEditText 判断城市编辑栏的文本信息 * @param text 存放文本框信息对应的城市编码 * @return 文本是否正确, 若是更新文本框信息不正确则不修改编码存储容器传入时的值 */ bool judgeCityEditText(QString &text); /** * @brief sendWeatherRequest 发送天气信息请求函数 */ void sendWeatherInfoRequest(); /** * @brief responseMenuAction 响应右键菜单 * @param action 右键菜单点击对象 */ void responseMenuAction(QAction *action); private slots: /** * @brief on_recvNetworkReply 接收API返回数据对象槽函数 * @param reply 包含API数据的对象 */ void on_recvNetworkReply(QNetworkReply *reply); /** * @brief on_switchModeBtn_clicked 模式切换按钮槽函数 * @param checked 当前按钮选中状态 */ void on_switchModeBtn_clicked(bool checked); /** * @brief on_forecastBtnClicked 预报天气界面按钮点击槽函数 * @param index 点击按钮的索引 */ void on_forecastBtnClicked(int index); /** * @brief on_provinceComboBox_currentIndexChanged 省份下拉列表框改变槽函数 * @param arg1 改变的文本 */ void on_provinceComboBox_currentIndexChanged(const QString &arg1); /** * @brief on_cityComboBox_currentIndexChanged 城市下拉列表框改变槽函数 * @param arg1 改变的文本 */ void on_cityComboBox_currentIndexChanged(const QString &arg1); /** * @brief on_countyComboBox_currentIndexChanged 区县下拉列表框改变槽函数 * @param arg1 改变的文本 */ void on_countyComboBox_currentIndexChanged(const QString &arg1); /** * @brief on_customContextMenuRequested 定制菜单信号槽函数 */ void on_customContextMenuRequested(); /** * @brief on_cityEdit_textChanged 城市编辑框文本改变槽函数 * @param arg1 改变的文本 */ void on_cityEdit_textChanged(const QString &arg1); private: Ui::CWeatherForecast *ui; QNetworkAccessManager *m_networkAccMgr; // 访问天气查询API的网络对象 stApiInfo m_stApiInfo; // api信息结构体对象 stWeatherInfo m_stWeatherInfo; // 天气信息结构体 CForecastWidget *m_forecastWgt; // 预报天气信息图表显示板块 QMap<QString, QMap<QString, QStringList>> m_cityInfoMap; // 城市信息容器 <省份,<城市,区县>> QMap<QString, QString> m_codeInfoMap; // 编码信息容器 <城市,编码> QCompleter *m_completer; // 过滤器对象 }; #endif // CWEATHERFORECAST_H

CWeatherForecast.cpp

#include "CWeatherForecast.h" #include "ui_CWeatherForecast.h" #include <QNetworkReply> #include <QUrl> #include <QMessageBox> #include <QDebug> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> #include <QFile> #include <QDomDocument> #include <QApplication> #include <QDir> #include <QStringListModel> CWeatherForecast::CWeatherForecast(QWidget *parent) : QMainWindow(parent) , ui(nullptr) , m_networkAccMgr(nullptr) , m_forecastWgt(nullptr) , m_completer(nullptr) { } CWeatherForecast::~CWeatherForecast() { } int CWeatherForecast::initialize() { // 定义返回对象 int ret = 0; // 初始化ui if(nullptr == ui) { ui = new Ui::CWeatherForecast; ui->setupUi(this); // 设置右键菜单信号发送 this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); connect(this, &CWeatherForecast::customContextMenuRequested , this, &CWeatherForecast::on_customContextMenuRequested); ret |= 0x1; } // 初始化网络访问对象 if(nullptr == m_networkAccMgr) { // 创建网络访问对象空间 m_networkAccMgr = new QNetworkAccessManager(this); // 连接接受网络返回的信号槽 connect(m_networkAccMgr, SIGNAL(finished(QNetworkReply *)) , this, SLOT(on_recvNetworkReply(QNetworkReply *))); ret |= 0x10; } // 初始化预报信息图表模块 if(nullptr == m_forecastWgt) { m_forecastWgt = new CForecastWidget; m_forecastWgt->initialize(); // 链接信号槽 connect(m_forecastWgt, &CForecastWidget::forecastBtnClickedSig, [=](int index) { loadForeCastInfo(m_stWeatherInfo.stForecastInfoList.at(index)); }); ret |= 0x100; } // 创建过滤器对象 if(nullptr == m_completer) { m_completer = new QCompleter; ret |= 0x1000; } // 加载城市信息配置文件 QString fileName = "./Config/CityCodeInfo.xml"; if(!loadConfig(fileName)) { QMessageBox::information(this, "提示", fileName + ":文件加载失败"); } // 加载天气预报配置文件 fileName = "./Config/WeatherApi.xml"; if(!loadConfig(fileName)) { QMessageBox::information(this, "提示", fileName + ":文件加载失败"); } // 通过返回值对象判断所有对象是否初始化完成,然后做关联设置操作 if(0x1111 == ret) { // 隐藏城市编辑框 ui->cityEdit->hide(); // 将预报天气信息控件添加到ui中 ui->forecastLayout->addWidget(m_forecastWgt); ui->forecastLayout->setStretch(0, 3); ui->forecastLayout->setStretch(1, 5); // 创建提示信息列表容器,并添加城市名称信息 QStringList listTemp = m_codeInfoMap.keys(); // 追加城市编码信息 listTemp.append(m_codeInfoMap.values()); // 创建提示信息存储的数据模板,并指定父对象 QStringListModel *model = new QStringListModel(m_completer); // 将提示信息设置到模板中 model->setStringList(listTemp); // 将模板设置到过滤器对象中 m_completer->setModel(model); // 将过滤器对象设置到城市编辑栏中 ui->cityEdit->setCompleter(m_completer); // 设置省份下拉框 ui->provinceComboBox->addItems(m_cityInfoMap.keys()); } return ret; } int CWeatherForecast::unInitialize() { // 定义返回对象 int ret = 0; // 释放过滤器对象 if(nullptr != m_completer) { delete m_completer; m_completer = nullptr; ret |= 0x1; } // 释放预报信息对象 if(nullptr != m_forecastWgt) { m_forecastWgt->unInitialize(); delete m_forecastWgt; m_forecastWgt = nullptr; ret |= 0x10; } // 释放API访问对象 if(nullptr != m_networkAccMgr) { delete m_networkAccMgr; m_networkAccMgr = nullptr; ret |= 0x100; } // 释放ui if(nullptr != ui) { delete ui; ui = nullptr; ret |= 0x1000; } return ret; } bool CWeatherForecast::loadWeatherForecastCfg(QString path) { bool ret = false; do { // 指定文件并打开 QFile file(path); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::information(this, "提示", path + ":文件打开失败"); ret = false; break; } // 创建QDomDocument对象并设置文档类型名 QDomDocument domDoc; // 设置domDoc的上下文 if(!domDoc.setContent(&file)) { // 上下文设置失败,关闭QFile对象打开的文件 file.close(); QMessageBox::information(this, "提示", path + ":QDomDocument对象上下文设置失败"); ret = false; break; } // 上下文设置成功不再使用QFile对象打开的文件,将其关闭 file.close(); // 从QDomDocument对象中取到对应的顶级节点元素对象 QDomElement apiInfo = domDoc.firstChildElement("Root"); // 判断顶级节点元素对象是否为空 if(apiInfo.isNull()) { QMessageBox::information(this, "提示", path + ":ApiInfo节点元素对象获取失败"); ret = false; break; } // 一次获取指定的apiInfo子节点,并且获取其值 m_stApiInfo.link = apiInfo.firstChildElement("Link").attribute("Link"); m_stApiInfo.apiKey = apiInfo.firstChildElement("ApiKey").attribute("ApiKey"); m_stApiInfo.cityCode = apiInfo.firstChildElement("CityCode").attribute("CityCode"); m_stApiInfo.extensions = apiInfo.firstChildElement("Extensions").attribute("Extensions"); // 返回值变为true ret = true; }while(false); return ret; } bool CWeatherForecast::readCityInfo(const QDomElement &root) { // 创建省份名对象 QString province; // 创建城市名对象 QString city; // 获取首个城市信息节点 QDomElement element = root.firstChildElement("CityInfo"); // 遍历root节点中的城市信息子节点 while(!element.isNull()) { // 获取城市名和城市编码 QString name = element.attribute("Name"); QString code = element.attribute("Code"); // 判断城市名和城市编码不为空 if(code.endsWith("0000")) { // 省份对象赋值 province = name; // 将省份编码信息填入容器 m_codeInfoMap[province] = code; } else if(code.endsWith("00")) { // 城市对象赋值 city = name; } else { // 区县值添加 m_cityInfoMap[province][city].append(name); // 添加区县编码信息 code = code.endsWith("01") ? code.replace(code.size() - 2, 2, "00") : code; m_codeInfoMap[name] = code; } // 获取下一个信息子节点 element = element.nextSiblingElement(); } // 判断容器是否为空并返回 return !m_cityInfoMap.isEmpty() && !m_codeInfoMap.isEmpty(); } bool CWeatherForecast::loadConfig(QString path) { bool ret = false; do { // 指定文件并打开 QFile file(path); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::information(this, "提示", path + ":文件打开失败"); ret = false; break; } // 创建QDomDocument对象并设置文档类型名 QDomDocument domDoc; // 设置domDoc的上下文 if(!domDoc.setContent(&file)) { // 上下文设置失败,关闭QFile对象打开的文件 file.close(); QMessageBox::information(this, "提示", path + ":QDomDocument对象上下文设置失败"); ret = false; break; } // 上下文设置成功不再使用QFile对象打开的文件,将其关闭 file.close(); // 从QDomDocument对象中取到对应的顶级节点元素对象 QDomElement root = domDoc.firstChildElement("Root"); // 判断顶级节点元素对象是否为空 if(root.isNull()) { QMessageBox::information(this, "提示", path + ":root节点元素对象获取失败"); ret = false; break; } QString type = root.attribute("Type"); if(0 == type pare("CityInfo")) { // 获取返回值 ret = readCityInfo(root); } else if(0 == type pare("ApiInfo")) { // 获取返回值 ret = readAPiInfo(root); } }while(false); return ret; } bool CWeatherForecast::readAPiInfo(const QDomElement &root) { // 一次获取指定的apiInfo子节点,并且获取其值 m_stApiInfo.link = root.firstChildElement("Link").attribute("Link"); m_stApiInfo.apiKey = root.firstChildElement("ApiKey").attribute("ApiKey"); m_stApiInfo.cityCode = root.firstChildElement("CityCode").attribute("CityCode"); m_stApiInfo.extensions = root.firstChildElement("Extensions").attribute("Extensions"); // 判断节点值是否存在空值 return !m_stApiInfo.fieldIsEmpty(); } int CWeatherForecast::parseJson(QByteArray jsonData) { // 创建返回值对象 int ret = 0; //! 创建QJsonParseError对象,用于判断Json是否正确 //! 尽管从API中返回的Json基本正确,但是流程还是要走,养成好习惯 QJsonParseError jsonError; // 将json数据和jsonError对象传入fromJson函数中 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &jsonError); // 判断错误码是否不等于QJsonParseError::NoError,不等于则返回 if(jsonError.error != QJsonParseError::NoError) { return ret; } // 创建QJsonObject对象接收object的返回值 QJsonObject jsonObj = jsonDoc.object(); //! 判断接收的Json串是否正确 //! status为1那么状态为成功,infocode为10000则返回状态为正确 if("1" != jsonObj.value("status").toString() || "10000" != jsonObj.value("infocode").toString()) { QMessageBox::warning(nullptr, "警告", "数据返回失败/错误"); return ret; } // 获取当前模式按钮的选中状态 bool isChecked = ui->switchModeBtn->isChecked(); // 通过模式切换按钮判断获取当前模式 QString infoType = isChecked ? "forecasts" : "lives"; //! 通过infoType获取对应的内容 //! 因为infoType下的标识符为‘[’所以先转换为数组 //! 然后取数组下第一个元素并将其转为QJsonObject对象 QJsonObject weatherInfo = jsonObj.value(infoType).toArray().at(0).toObject(); // 读取基础信息(省份、城市、区域编码、报告时间) m_stWeatherInfo.setBaseInfo(weatherInfo.value("province").toString() , weatherInfo.value("city").toString() , weatherInfo.value("adcode").toString() , weatherInfo.value("reporttime").toString()); // 通过按钮选中状态使用不同模式的转换函数 if(isChecked) { // 预报天气转换 parseForecastJson(weatherInfo); } else { // 实时天气转换 parseLiveJson(weatherInfo); } return ret; } void CWeatherForecast::parseLiveJson(QJsonObject weatherInfo) { // 获取天气 QString weather = weatherInfo.value("weather").toString(); // 获取温度 QString temperature = weatherInfo.value("temperature").toString(); // 获取风向 QString winddirection = weatherInfo.value("winddirection").toString(); // 获取风力 QString windpower = weatherInfo.value("windpower").toString(); // 获取湿度 QString humidity = weatherInfo.value("humidity").toString(); // 设置信息 m_stWeatherInfo.stliveInfo.setWeatherInfo(weather, temperature , winddirection, windpower, humidity); if(m_stWeatherInfo.stliveInfo.fieldIsEmpty()) { QMessageBox::information(this, "提示", "没有新的天气信息"); return; } loadUiInfo(false); } void CWeatherForecast::parseForecastJson(QJsonObject weatherInfo) { // 每次添加将上次的预报天气信息清空 m_stWeatherInfo.stForecastInfoList.clear(); // 获取cast的内容,并将其转为数组 QJsonArray forecastArray = weatherInfo.value("casts").toArray(); // 获取json数组对象的迭代器 QJsonArray::const_iterator iterator = forecastArray.begin(); // 判断获取的起始迭代器不等于结束迭代器 if(forecastArray.end() == iterator) { QMessageBox::information(this, "提示", "没有新的天气信息"); return; } // 遍历天气信息 for(; iterator != forecastArray.end(); ++iterator) { // 先追加一个预报结构体 m_stWeatherInfo.stForecastInfoList.append(stForecastsInfo()); // 获取最新追加结构体的引用对象 stForecastsInfo &forecastsInfo = m_stWeatherInfo.stForecastInfoList.last(); // 获取当前内容的QJsonObject对象 QJsonObject forecastsInfoObj = (*iterator).toObject(); // 设置日期 forecastsInfo.setDate(forecastsInfoObj.value("date").toString()); // 设置天气 forecastsInfo.setWeather(forecastsInfoObj.value("dayweather").toString() , forecastsInfoObj.value("nightweather").toString()); // 设置温度 forecastsInfo.setTemperature(forecastsInfoObj.value("daytemp").toString() , forecastsInfoObj.value("nighttemp").toString()); // 设置风向 forecastsInfo.setWindDirection(forecastsInfoObj.value("daywind").toString() , forecastsInfoObj.value("nightwind").toString()); // 设置风力 forecastsInfo.setWindPower(forecastsInfoObj.value("daypower").toString() , forecastsInfoObj.value("nightpower").toString()); } //加载ui信息 loadUiInfo(true); } void CWeatherForecast::loadUiInfo(bool isChecked) { // 设置通用属性 ui->reportTimeLab->setText(m_stWeatherInfo.reporttime); // 城市字符串 省份+城市 QString cityStr = m_stWeatherInfo.province + "-" +m_stWeatherInfo.city; // 设置预报天气信息 if(isChecked) { // 设置预报天气城市 ui->forecastWeatherCityLab->setText(cityStr); // 更新预报图表模块信息 m_forecastWgt->updateWeatherInfo(m_stWeatherInfo.stForecastInfoList); // 加载当前显示的天气信息 loadForeCastInfo(m_stWeatherInfo.stForecastInfoList.first()); } // 设置实时天气信息 else { // 设置实时天气城市 ui->liveWeatherCityLab->setText(cityStr); // 获取实时信息的引用 const stLiveInfo &info = m_stWeatherInfo.stliveInfo; // 设置实时信息 ui->liveTempLab->setText(info.temperature + "°C"); ui->liveWeatherLab->setText(info.weather); ui->liveHumidityLab->setText(info.humidity); ui->liveLindPowerLab->setText(info.windpower); ui->liveWindDirectionLab->setText(info.winddirection); } } void CWeatherForecast::loadForeCastInfo(const stForecastsInfo &info) { // 将ui上对应的信息设置 ui->forecastWeatherLab->setText(info.dayweather); ui->forecastHighTempLab->setText(info.daytemp + "°C"); ui->forecastLowTempLab->setText(info.nighttemp + "°C"); ui->forecastWindPowerLab->setText(info.daypower); ui->forecastWindDirectionLab->setText(info.daywind); ui->forecastDateLab->setText(info.date + " " +info.week); } bool CWeatherForecast::judgeCityEditText(QString &text) { // 创建返回值对象 bool ret = false; // 获取城市文本 QString cityCode = ui->cityEdit->isHidden() ? ui->countyComboBox->currentText() : ui->cityEdit->text(); // 判断文本是否为空 if(cityCode.isEmpty()) { QMessageBox::information(this, "提示", "请输入城市名称或城市编码"); } else { // 判断城市名称中是否包含文本 if(m_codeInfoMap.keys().contains(cityCode)) { // 获取对应城市名的城市编码并赋值 text = m_codeInfoMap[cityCode]; ret = true; } // 获取城市编码是否包含文本框文本 else if(m_codeInfoMap.values().contains(cityCode)) { // 城市编码正确并赋值 text = cityCode; ret = true; } else { QMessageBox::information(this, "提示", "请输入正确的城市名称或城市编码"); } } return ret; } void CWeatherForecast::on_recvNetworkReply(QNetworkReply *reply) { // 判断错误码是否为QNetworkReply::NoError(若判断条件成立,则reply对象数据错误) if(reply->error() != QNetworkReply::NoError) { // 弹出警告 QMessageBox::warning(nullptr, "警告", "数据返回错误"); } else { // 创建array容器接收数据 QByteArray data; // 读取所有json串 data = reply->readAll(); // 解析json串 parseJson(data); } // 释放内存,防止内存泄漏 delete reply; reply = nullptr; } void CWeatherForecast::sendWeatherInfoRequest() { if(judgeCityEditText(m_stApiInfo.cityCode)) { // 调用组合API链接函数申请API QUrl url(m_stApiInfo.joinApiLink()); // 获取天气预报回执;此处返回的QNetworkReply对象将在on_recvNetworkReply槽函数中体现,可不在此接收释放。 m_networkAccMgr->get(QNetworkRequest(url)); } } void CWeatherForecast::responseMenuAction(QAction *action) { // 当参数值为空时返回 if(nullptr == action) { return; } QString text = action->text(); if("刷新" == text) { // 发送天气信息请求 sendWeatherInfoRequest(); } else { // 获取城市编辑栏当前显示状态 bool isHidden = ui->cityEdit->isHidden(); // 将城市编辑栏显示状态取反设置 ui->cityEdit->setHidden(!isHidden); // 设置城市选择模式显隐状态 ui->selectModeWgt->setHidden(isHidden); if(isHidden) { // 获取选择模式下当前城市名 QString text = ui->countyComboBox->currentText(); // 将当前城市名设置到编辑栏中 ui->cityEdit->setText(text); } else { // 获取当前区县编码值 QString countyCode = m_stApiInfo.cityCode; // 通过当前区县编码值获取当前区县名 QString countyName = m_codeInfoMap.key(m_stApiInfo.cityCode); bool contain = m_codeInfoMap.values().contains(countyCode); // 通过当前区县编码值获取当前城市编码值 QString cityCode = countyCode.replace(countyCode.size() - 2, 2, "00"); contain = m_codeInfoMap.values().contains(cityCode); // 通过省份编码值获取省份名 QString cityName = m_codeInfoMap.key(cityCode); // 包含市辖区时移除市辖区文本 cityName = cityName.replace("市辖区", ""); // 通过当前区县编码值获取当前省份编码值 QString provinceCode = countyCode.replace(countyCode.size() - 4, 4, "0000"); contain = m_codeInfoMap.values().contains(provinceCode); // 通过省份编码值获取省份名 QString provinceName = m_codeInfoMap.key(provinceCode); // 然后将获取到的三个值更新到下拉框中 // 设置省份 ui->provinceComboBox->setCurrentText(provinceName); // 设置城市 ui->cityComboBox->setCurrentText(cityName); // 设置区县 ui->countyComboBox->setCurrentText(countyName); } } } void CWeatherForecast::on_switchModeBtn_clicked(bool checked) { // 判断选中状态,获取将要设置对应的文本 QString text = checked ? "预报天气" : "实时天气"; // 设置当前显示的文本 ui->switchModeBtn->setText(text); // 设置API扩展参数变量的新值 m_stApiInfo.extensions = checked ? "all" : "base"; // 设置当前栈模式对应的窗口 ui->stackedWidget->setCurrentIndex(checked ? 1 : 0); // 发送天气信息请求 sendWeatherInfoRequest(); } void CWeatherForecast::on_forecastBtnClicked(int index) { if(index < m_stWeatherInfo.stForecastInfoList.size()) { loadForeCastInfo(m_stWeatherInfo.stForecastInfoList.at(index)); } } void CWeatherForecast::on_provinceComboBox_currentIndexChanged(const QString &arg1) { //! 当省份变化时,城市下拉列表框将要清空 ui->cityComboBox->clear(); //! 然后添加对应的城市列表 ui->cityComboBox->addItems(m_cityInfoMap[arg1].keys()); } void CWeatherForecast::on_cityComboBox_currentIndexChanged(const QString &arg1) { // 当当前文本内容为空时返回 if(arg1.isEmpty()) { return; } //! 当城市变化时,区/县下拉列表框将要清空 ui->countyComboBox->clear(); //! 然后添加对应的区县列表 ui->countyComboBox->addItems(m_cityInfoMap[ui->provinceComboBox->currentText()][arg1]); } void CWeatherForecast::on_countyComboBox_currentIndexChanged(const QString &arg1) { // 当当前文本内容为空时返回 if(arg1.isEmpty()) { return; } // 区县改变时将要 m_stApiInfo.cityCode = m_codeInfoMap[arg1]; // 发送天气信息请求 sendWeatherInfoRequest(); } void CWeatherForecast::on_customContextMenuRequested() { // 创建菜单变量 QMenu menu; // 添加菜单选项 menu.addAction(new QAction("刷新", &menu)); menu.addAction(new QAction("切换城市选择模式", &menu)); // 显示菜单,并且显示位置为鼠标位置,并接收返回的对象 QAction *action = menu.exec(QCursor::pos()); responseMenuAction(action); } void CWeatherForecast::on_cityEdit_textChanged(const QString &arg1) { // 当城市编码容器包含当前文本Key值则进入 if(m_codeInfoMap.contains(arg1) || m_codeInfoMap.values().contains(arg1)) { // 发送天气信息请求 sendWeatherInfoRequest(); } }

CWeatherForecast.ui

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>CWeatherForecast</class> <widget class="QMainWindow" name="CWeatherForecast"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>649</width> <height>450</height> </rect> </property> <property name="windowTitle"> <string>CWeatherForecast</string> </property> <widget class="QWidget" name="centralWidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QHBoxLayout" name="horizontalLayout_7"> <item> <widget class="QPushButton" name="switchModeBtn"> <property name="text"> <string>实时天气</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QLineEdit" name="cityEdit"> <property name="placeholderText"> <string>请输入城市/城市编码</string> </property> </widget> </item> <item> <widget class="QWidget" name="selectModeWgt" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QComboBox" name="provinceComboBox"/> </item> <item> <widget class="QComboBox" name="cityComboBox"/> </item> <item> <widget class="QComboBox" name="countyComboBox"/> </item> </layout> </widget> </item> <item> <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="label_4"> <property name="text"> <string>报告时间:</string> </property> </widget> </item> <item> <widget class="QLabel" name="reportTimeLab"> <property name="text"> <string>报告时间</string> </property> </widget> </item> </layout> </item> <item> <widget class="QStackedWidget" name="stackedWidget"> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="page"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="3,1"> <property name="spacing"> <number>0</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0" columnstretch="1,1,3"> <property name="spacing"> <number>0</number> </property> <item row="0" column="0"> <widget class="QLabel" name="liveWeatherCityLab"> <property name="text"> <string>城市标签</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLabel" name="liveTempLab"> <property name="text"> <string>°C</string> </property> </widget> </item> <item row="0" column="2" rowspan="2"> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="1" column="0"> <widget class="QLabel" name="liveWeatherImgLab"> <property name="text"> <string>天气图标</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QLabel" name="liveWeatherLab"> <property name="text"> <string>天气</string> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="lab"> <property name="text"> <string>风速:</string> </property> </widget> </item> <item> <widget class="QLabel" name="liveLindPowerLab"> <property name="text"> <string>风速</string> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_4"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="label_11"> <property name="text"> <string>风向:</string> </property> </widget> </item> <item> <widget class="QLabel" name="liveWindDirectionLab"> <property name="text"> <string>风向</string> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_5"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="label_12"> <property name="text"> <string>湿度:</string> </property> </widget> </item> <item> <widget class="QLabel" name="liveHumidityLab"> <property name="text"> <string>湿度</string> </property> </widget> </item> </layout> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="page_2"> <layout class="QVBoxLayout" name="forecastLayout"> <property name="spacing"> <number>0</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QGridLayout" name="gridLayout"> <property name="rightMargin"> <number>80</number> </property> <item row="1" column="0"> <widget class="QLabel" name="forecastWeatherImgLab"> <property name="text"> <string>天气图标</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QLabel" name="forecastHighTempLab"> <property name="text"> <string>°C</string> </property> </widget> </item> <item row="1" column="2"> <layout class="QHBoxLayout" name="horizontalLayout_8"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="lab_2"> <property name="text"> <string>风速:</string> </property> </widget> </item> <item> <widget class="QLabel" name="forecastWindPowerLab"> <property name="text"> <string>风速</string> </property> </widget> </item> </layout> </item> <item row="2" column="1"> <widget class="QLabel" name="forecastLowTempLab"> <property name="text"> <string>°C</string> </property> </widget> </item> <item row="2" column="2"> <layout class="QHBoxLayout" name="horizontalLayout_9"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="label_14"> <property name="text"> <string>风向:</string> </property> </widget> </item> <item> <widget class="QLabel" name="forecastWindDirectionLab"> <property name="text"> <string>风向</string> </property> </widget> </item> </layout> </item> <item row="0" column="1"> <widget class="QLabel" name="forecastDateLab"> <property name="text"> <string>星期 日期</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="forecastWeatherCityLab"> <property name="text"> <string>城市标签</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="forecastWeatherLab"> <property name="text"> <string>天气</string> </property> </widget> </item> </layout> </item> </layout> </widget> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menuBar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>649</width> <height>23</height> </rect> </property> </widget> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui> 总结

本文通过高德开放平台的天气预报模块获取天气预报信息,然后通过Qt各个类实现天气预报的基本功能。因为各个平台的差异,如返回的天数、天气Json格式、信息等各不相同,所以文本也就固定了仅支持高德开放平台提供的天气预报接口。总的来说,功能不算复杂,比较适合新手练习使用. 然后是汇报一下近况,为什么更新变慢了,其一是近期公司项目进入收尾,加班增多;其次则是生活和天气原因,再加上每日私人编程时间有限等情况的影响。但我还是会持续更新,下一章为"天气预报-界面优化篇",敬请期待叭。

相关文章 天气预报系列

Qt之天气预报实现(一)预备篇

相关类的基本使用

Qt读取Json文件(含源码+注释) Qt读写XML文件(含源码+注释) Qt之QChart各个图表的简单使用(含源码+注释) Qt创建右键菜单的两种通用方法(QTableView实现右键菜单,含源码+注释) Qt之QCompleter的简单使用(自动补全、文本框提示、下拉框提示含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧 (创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。 注:如有侵权,请联系作者删除

标签:

Qt之天气预报——功能实现篇(含源码+注释)由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Qt之天气预报——功能实现篇(含源码+注释)