WeatherWebService网站提供了获取天气数据的访问接口,不过对于免费的用户来讲,一天只能访问50次,超出之后需要付费。本身就提供了查询城市ID,支持城市列表,通过城市ID可以找到对应城市的照片等多个接口。当然,通过这个接口,可获取天气数据到本地。然后对天气数据进行分析,最终显示到界面上。最终的话可以把界面做的漂亮一点,把功能完善一下,甚至可以做一些可视化的数据分析,提供出行建议等。由于上网不是很方便,这里只做了基本的功能,界面也做的略显ugly。除了访问接口之外,网站还提供了接口说明,天气图标,城市图标等资源。
对于用户来讲,呈现的功能有,输入全国任一城市,单击查询按钮,界面将显示实时的温度、日期、图标;当天的天气情况、温度范围、风力大小、以及一些温馨提示;关于城市的简介、城市的图片(我没做)。默认情况下显示的是西安的天气情况。
1.从网站接口获取天气数据,使用到的是Qt实现Http请求的知识,即本地计算机作为客户端向该服务器发送Http请求,服务器响应天气数据,因此客户端需要接收并保存。发送Http请求的方式有多种,可以使用Post带参数(指哪个城市)结合网站提供的getweather方法来访问,也可以使用get请求,直接将城市名作为Url的一部分,交给Qt来解析。我用的是第二种方法。
2.获取到的天气数据其实是xml文件,用到的是C7—Qt中使用XML格式文档的知识,就是如何读取本地的xml文件,从中获得可用信息并存储到全局变量中。天气数据xml的构成很简单,一共两级目录,第一级是一些简介,无关痛痒;第二级是天气信息,节点名字统统叫做QString,文本值即有效信息。通过数数的方式判断数据内容。如下图所示。
3.根据全局变量中的数据刷新界面,多数界面部件是QLabel,该显示图的显示图,显示字的显示字即可。具体的步骤结合代码及其注释。
提供几个基于本案例可以拓展的点,和一些开发过程中遇到的值得注意的问题。
1.关于软件功能,界面可仿其他的天气预报软件稍加藻饰,可以结合Qt可视化数据分析做一些统计分析的部件,可以计算一下农历显示。有一个城市风貌的图片我没做,可以结合WeatherWebService网站提供的资源完善一下。
2.关于软件实现,可以尝试使用post带参请求,了解更多网络知识。可以使用xml的第二种stream的方式读取文件。可以尝试其他网站的接口。
3.QLabel设置Text值的时候请注意,给的参数为QString,这个QString过长的话,部件不换行会拉的很长。让部件与文本自适应,自动换行,自动调整大小。如下:
labelName->adjustSize();//控件大小与内容自适应
labelName->setWordWrap(true);//自动换行
labelName->setAlignment(Qt::AlignTop);
4.Qt进行文件读写的IO操作,请注意,文件名有特殊字符,将会导致QIODevice::write (QFile, “woshi.xml”): device not open。另外还要注意,从服务器获取数据,相当于是服务器回来的数据要写入本地文件,对于天气数据的分析相当于是要读取同一个本地文件。那必须保证读操作与写操作之间不发生冲突,否则会出现各种错误。如何保证呢?可以等服务器数据传输finish,即发出fnish信号的时候设置状态标志,使用定时器或者线程监测这个标志,直到写完在进行读操作。也可以在finish信号的槽函数中调用分析数据的函数。对于这个小案例,用的是第二种方法。
5.天气数据xml中的标点符号是中文输入的,在数据分析时要注意。使用到xml和network的类,记得现在.pro文件中
#include "widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QNetworkAccessManager *manager;
QNetworkReply *reply;
QFile *myFile;//xml写
QString str_utl;//网址字符串
QString filename;//xml文件名
QString def_city;//默认城市名
QString iconPath;//天气图标ICON
QString strProvince;//省份
QString strCity;//城市名
QString strTempr;//温度范围
QString strWeather;//今日天气
QString strIcon;//图标名字
QString strWind;//风力
QString strCurWeather;//实时天气
QString strTips;//温馨提示
QString strCityHistory;//城市历史
void Init();//初始化函数
void myHttpGetWeather(QString);//http请求下载xml天气文件
void myiRefreshUi();//根据xml文件的解析结果刷新界面
bool myDecodeXml();//解析xml文件
private slots:
void doProcessReadyRead();
void doProcessError(QNetworkReply::NetworkError);
void doProcessFinished();
void on_query_btn_clicked();
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
Init();
}
Widget::~Widget()
{
delete ui;
}
void Widget::Init()
{
str_utl.clear();
filename.clear();
def_city.clear();
strProvince.clear();
strCity.clear();
strWind.clear();
strTempr.clear();
strWeather.clear();
strIcon.clear();
strCurWeather.clear();
strTips.clear();
strCityHistory.clear();
iconPath.clear();
ui->lab_cityHistory->adjustSize();//控件大小与内容自适应
ui->lab_cityHistory->setWordWrap(true);//自动换行
ui->lab_cityHistory->setAlignment(Qt::AlignTop);
iconPath = QString(":/myWeather/");
str_utl.append("http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=");
filename = QString("woshi.xml");//获取到的天气数据
def_city = "西安";
myFile = new QFile(this);
myFile->setFileName(this->filename);//获取或创建文件
manager = new QNetworkAccessManager(this);
if(filename.isEmpty()){
return;
}
//获取默认城市 上海市的天气,并据此初始化各个界面部件
myHttpGetWeather(def_city);
//各界面部件初始化
// myiRefreshUi();
}
void Widget::myHttpGetWeather(QString str_city)//str = 城市名
{
QString current_addr;
QUrl myUrl;
QNetworkRequest myReq;//准备网络请求
current_addr.clear();
current_addr.append(str_utl).append(str_city);//当前查询城市网址
myUrl.setUrl(current_addr);//设置网络请求的url
myReq.setRawHeader(QByteArray("User-Agent"),QByteArray("MyOwnBrowser 1.0"));//设置http包的请求头
myReq.setUrl(myUrl);
bool ret = myFile->open(QIODevice::WriteOnly|QIODevice::Truncate);//以重写的方式打开,在写入新的数据时会将原有
if(!ret){
QMessageBox::warning(this,"warning","myHttpGetWeather失败!");
return;
}
reply = manager->get(myReq);//发送http get请求
connect(reply,SIGNAL(readyRead()),this,SLOT(doProcessReadyRead()));//数据来临的信号,IO操作大多数都是这个信号
connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(doProcessError(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(finished()),this,SLOT(doProcessFinished()));//传输(一次应答)完成
}
//根据所得xml天气信息刷新界面
void Widget::myiRefreshUi()
{
//获取当前时间
QDateTime c_time = QDateTime::currentDateTime();
QString str_week = c_time.toString("ddd");//获取星期
QDate date = QDate::currentDate();
QString labDataTime;
labDataTime.append(QString::number(date.year()));
labDataTime.append("年");
labDataTime.append(QString::number(date.month()));
labDataTime.append("月");
labDataTime.append(QString::number(date.day()));
labDataTime.append("日");
labDataTime.append(" ");
labDataTime.append(str_week);
//获取xml天气信息
if(!myDecodeXml()){
return;
}
//刷新界面
//0.刷新省份城市
QString tempCity;
tempCity = strProvince.append(" ");
tempCity = tempCity.append(strCity);
ui->url_edit->setText(tempCity);
//1.刷新时间界面
ui->lab_Datatime->setText(labDataTime);
//2.刷新天气图标
QString tempIconPath = iconPath;
tempIconPath.append(strIcon);
qDebug()<<tempIconPath;
QPixmap pix(tempIconPath);
pix = pix.scaled(QSize(100,100));
ui->lab_wetherIcon->setPixmap(pix);
//3.刷新实时温度值
qDebug()<<"实时天气情况:"<<strCurWeather;
strCurWeather.remove("今日天气实况:");
QStringList list1 = strCurWeather.split(";");//此处是中文输入下的分号
QString tempTempr=list1.at(0);
QStringList list2 = tempTempr.split(":");//此处是中文输入下的冒号
QString realTempr = list2.at(1);
qDebug()<<list1.at(0)<<list2.at(1);
ui->lab_realTemp->setText(realTempr);
//4.刷新温度值
ui->lab_tempRange->setText(strTempr);
//5.刷新今日天气
ui->lab_wethStatus->setText(strWeather);
//6.刷新风力
ui->lab_wind->setText(strWind);
//7.刷新温馨提示
ui->lab_tips->setText(strTips);
//8.刷新城市历史
ui->lab_cityHistory->setText(strCityHistory);
//9.刷新城市照片
}
//解析xml文件,为全局变量赋值
bool Widget::myDecodeXml()
{
if(myFile->isOpen()){
myFile->close();
}
bool ret = myFile->open(QIODevice::ReadWrite);//以重写的方式打开,在写入新的数据时会将原有
if(!ret){
QMessageBox::warning(this,"warning","myDecodeXml打开失败!");
return false;
}
QDomDocument doc("yxx");//定义doc对象,初始化名字
QString error;
int line, column;
bool isLot = doc.setContent(myFile, &error, &line, &column);//将文件与QDomDocument类关联
if(!isLot){
//关联xml文件失败
myFile->close();
qDebug() << "Error:" << error << "in line " << line << "column" << column;
QMessageBox::warning(this,"waring","myDecodeXml关联失败");
return false;
}
myFile->close();
QDomElement firstElem = doc.documentElement();//获取到了一级目录
QDomNodeList secondList = firstElem.childNodes();//获取全部二级目录
for (int i = 0; i < secondList.count(); ++i) {
QDomElement secondElem = secondList.at(i).toElement();//获取二级目录节点的属性值管理者domElement
if(i==0){//获取省份
strProvince = secondElem.text();
}
if(i==1){//获取城市名
strCity = secondElem.text();
}
if(i==5){//获取温度
strTempr = secondElem.text();
}
if(i==6){//获取天气情况
QString tempStr;
tempStr = secondElem.text();
QStringList list = tempStr.split(" ");
strWeather = list.at(1);
}
if(i==7){//获取风力
strWind = secondElem.text();
}
if(i==8){//获取天气图标
QString tempIcon = secondElem.text();
QStringList list3 = tempIcon.split(".");
QString tempIcon2 = list3.at(0);
strIcon = tempIcon2.append(".png");
}
if(i==10){//获取实时天气情况
strCurWeather = secondElem.text();
}
if(i==11){//获取温馨提示
strTips = secondElem.text();
}
if(i==22){//获取城市介绍
strCityHistory = secondElem.text();
}
}
qDebug()<<"执行完毕xmldecode";
return true;
}
//查询按钮槽函数
void Widget::on_query_btn_clicked()
{
//获取地址
QString city = ui->url_edit->text();//获取城市名字
if(!city.isEmpty()){
myHttpGetWeather(city);//获取当前城市的xml天气文件
}
}
//数据来临槽函数
void Widget::doProcessReadyRead()
{
//读取应答数据,并且写入文件中
while(!reply->atEnd()){
QByteArray ba = reply->readAll();
myFile->write(ba);
}
}
//过程出错槽函数
void Widget::doProcessError(QNetworkReply::NetworkError err)
{
qDebug()<<"接收过程出错"<<reply->errorString();
qDebug()<<err;
}
//请求过程结束信号
void Widget::doProcessFinished()
{
qDebug()<<"接收数据完毕!";
myFile->close();//强制刷新
myiRefreshUi();
}