文章目录
一、功能概述
天气预报包含实时天气模式和预报天气模式。
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.com/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.com/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 if(!name.contains("市辖区")) // 不包含“市辖区”的名才添加
{
// 区县值添加
m_cityInfoMap[province][city].append(name);
// 添加区县编码信息
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.compare("CityInfo"))
{
// 获取返回值
ret = readCityInfo(root);
}
else if(0 == type.compare("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之天气预报——界面优化篇(含源码+注释)
相关类的基本使用
Qt读取Json文件(含源码+注释)
Qt读写XML文件(含源码+注释)
Qt之QChart各个图表的简单使用(含源码+注释)
Qt创建右键菜单的两种通用方法(QTableView实现右键菜单,含源码+注释)
Qt之QCompleter的简单使用(自动补全、文本框提示、下拉框提示含源码+注释)
友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)
注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除