这个项目的最终目的是实现在ARM板上,使用qt+opencv实现人脸识别的考勤系统。上一次完成了打开摄像头代码,这一次我打算在系统中加入通过网络实时获取当地天气情况的功能。
-
使用心知天气的API接口实现(免费)
-
在这里我选择使用心知天气提供的API接口来完成这次任务。JSON格式。
心知天气的官网网址
注册登录后,进入控制台,点击我的API项目
-
-
记录好自己的API私钥,这里被和谐了,因为私钥是重要的访问密码,最好不要透露出去。
因为是免费用户,所以只有3个API接口可以调用,其他的都不能用,访问频率也被限制到400次/小时,或者20次/分钟。(当然如果你是土豪可以考虑花钱购买)超过了访问频率,服务器将会拒绝你的访问,直到一个小时后再恢复。
然后点击产品文档查看API格式
-
-
点击天气现象代码说明这里,下载官方提供的图标,程序中会用到。至于使用哪个图标就看你自己喜好了。
-
-
接下来看API调用格式,因为是免费用户,可以使用的三个API分别是天气实况,逐日天气和生活指数。
-
第一个,天气实况
-
-
将这个url地址复制下来,将key=后面的打码部分改为自己的私钥,location=后面的beijing改为ip,其他的不用改。ip的意思是自动根据IP地址定位城市,如果想查询指定的城市,可以通过上面的“接口中的通用参数”了解,这一章对API中的参数设置有详细说明。JSON格式我就不细说了,自己去了解吧。
-
-
第二个,逐日天气
-
-
把url地址复制下来,参数修改如上。
第三个,生活指数
-
-
url地址复制下来。
API接口记录完毕,接下里开始写代码。
-
Qt重点代码解析
-
访问API接口,并获取返回的JSON数据
-
访问网址用到了QT的两个类,QNetworkAccessManager和QNetworkReply
记得包含头文件和在.pro文件中加入network
-
#include <QtNetwork> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkReply>
-
在.h文件中声明变量,因为有三个地址,我想同时访问,所以每一个都要创建对应的QNetworkAccessManager和QNetworkReply
-
QNetworkAccessManager *m_NetManger_now; QNetworkAccessManager *m_NetManger_daily; QNetworkAccessManager *m_NetManger_life; QNetworkReply *m_Reply_now; QNetworkReply *m_Reply_daily; QNetworkReply *m_Reply_life;
接着声明槽
-
private slots: void finishedSlot_now(QNetworkReply*); void finishedSlot_daily(QNetworkReply*); void finishedSlot_life(QNetworkReply*);
在.c的构造函数中加入
-
url_weather_now = "https://api.seniverse.com/v3/weather/now.json?key=你自己的私钥&location=ip&language=zh-Hans&unit=c"; url_weather_daily = "https://api.seniverse.com/v3/weather/daily.json?key=你自己的私钥&location=ip&language=zh-Hans&unit=c&start=0&days=5"; url_weather_life = "https://api.seniverse.com/v3/life/suggestion.json?key=你自己的私钥&location=ip&language=zh-Hans"; m_NetManger_now = new QNetworkAccessManager; m_NetManger_daily = new QNetworkAccessManager; m_NetManger_life = new QNetworkAccessManager; connect(m_NetManger_now,&QNetworkAccessManager::finished,this,&WeatherWidget::finishedSlot_now); connect(m_NetManger_daily,&QNetworkAccessManager::finished,this,&WeatherWidget::finishedSlot_daily); connect(m_NetManger_life,&QNetworkAccessManager::finished,this,&WeatherWidget::finishedSlot_life); m_Reply_now = m_NetManger_now->get(QNetworkRequest(url_weather_now)); m_Reply_life = m_NetManger_life->get(QNetworkRequest(url_weather_life)); m_Reply_daily = m_NetManger_daily->get(QNetworkRequest(url_weather_daily));
接着完成槽函数的编写,这里列举一个例子,其他的可以举一反三。
-
void WeatherWidget::finishedSlot_now(QNetworkReply *) { m_Reply_now->attribute(QNetworkRequest::HttpStatusCodeAttribute); m_Reply_now->attribute(QNetworkRequest::RedirectionTargetAttribute); if (m_Reply_now->error() == QNetworkReply::NoError) { QByteArray bytes = m_Reply_now->readAll(); QString data = QString::fromUtf8(bytes); qDebug() << "m_Reply_now: " << data; } else { qDebug() << m_Reply_now->errorString(); } m_Reply_now->deleteLater(); }
这时运行程序,可以看到qDebug()出来的东西,就是我们需要的JSON格式数据,但是还没有解析。那么接下来我们就来将这些数据从JSON格式中解析出来。
-
JSON格式的解析
-
QT5版本以后提供了JSON解析的库,我们可以直接通过QT进行调用。
加入头文件
-
#include <QJsonObject> #include <QJsonArray> #include <QJsonDocument> #include <QJsonValue>
这里为了方便数据处理,自定义了三个结构体进行管理
-
struct Weather_Now_Data { QString last_update; //最后一次更新时间 QString country; //国家 QString id; //ID号 QString name; //名字 QString path; //地点 QString timezone; //时区 QString timezone_offset; //时区偏移 QString code; //天气气象代码 QString temperature; //温度 QString text; //天气气象文字 }; struct Weather_Life_Data { QString car_washing; //洗车 QString dressing; //穿衣 QString flu; //感冒 QString sport; //运动 QString travel; //旅游 QString uv; //紫外线 }; struct Weather_Daily_Data { QString date; //返回日期 QString text_day; //白天天气现象文字 QString code_day; //白天天气现象代码 QString text_night; //晚间天气现象文字 QString code_night; //晚间天气现象代码 QString high; //当天最高温度 QString low; //当天最低温度 QString precip; //降水概率 范围0~100,单位百分比 QString wind_direction; //风向文字 QString wind_direction_degree; //风向角度 范围0~360 QString wind_speed; //风速,单位km/h QString wind_scale; //风力等级 };
接着在.h文件中定义变量
struct Weather_Now_Data weather_now; struct Weather_Daily_Data weather_daily_1, weather_daily_2, weather_daily_3; struct Weather_Life_Data weather_life;
定义三个JSON格式解析函数
-
void json_analysis_now(QByteArray data); //解析json格式现在天气 void json_analysis_daliy(QByteArray data); //解析json格式逐日时间 void json_analysis_life(QByteArray data); //解析json格式生活指数
解析JSON格式其实就是分解大括号和数组,下面我提供天气实况的解析代码,剩下的2个可以举一反三。你可以把我注释掉的qDebug()语句全部取消注释运行一次,然后看看输出结果,自己仔细研究一下就大概知道JSON格式是如何解析的了。
-
void WeatherWidget::json_analysis_now(QByteArray data) { QByteArray block; block = data; //qDebug() << "接收 : " << block; QJsonParseError json_error; QJsonDocument parse_doucment = QJsonDocument::fromJson(block, &json_error); //qDebug() << "parse_doucment : " << parse_doucment; //下面是json格式的解析 if(json_error.error == QJsonParseError::NoError) { if(parse_doucment.isObject()) { //开始解析json对象 QJsonObject jsonObject = parse_doucment.object(); if(jsonObject.contains("results")) { QJsonValue results_value = jsonObject.take("results"); if(results_value.isArray()) //判断他是不是json数组 { QJsonArray results_array = results_value.toArray(); QJsonObject results_object = results_array.at(0).toObject(); //qDebug() << "results_object : " << results_object; //提取last_update if(results_object.contains("last_update")) { weather_now.last_update = results_object.take("last_update").toString(); //qDebug() << "last_update : " << last_update; } //提取location if(results_object.contains("location")) { QJsonValue location_value = results_object.take("location"); //qDebug() << "location_value : " << location_value; QJsonObject location_object = location_value.toObject(); //提取country if(location_object.contains("country")) { weather_now.country = location_object.take("country").toString(); //qDebug() << "country : " << country; } //提取id if(location_object.contains("id")) { weather_now.id = location_object.take("id").toString(); //qDebug() << "id : " << id; } //提取name if(location_object.contains("name")) { weather_now.name = location_object.take("name").toString(); //qDebug() << "name : " << name; } //提取path if(location_object.contains("path")) { weather_now.path = location_object.take("path").toString(); //qDebug() << "path : " << path; } //提取timezone if(location_object.contains("timezone")) { weather_now.timezone = location_object.take("timezone").toString(); //qDebug() << "timezone : " << timezone; } //提取timezone_offset if(location_object.contains("timezone_offset")) { weather_now.timezone_offset = location_object.take("timezone_offset").toString(); //qDebug() << "timezone_offset : " << timezone_offset; } } //提取now if(results_object.contains("now")) { QJsonValue now_value = results_object.take("now"); //qDebug() << "now_value : " << now_value; QJsonObject now_object = now_value.toObject(); //提取code if(now_object.contains("code")) { weather_now.code = now_object.take("code").toString(); //qDebug() << "code : " << code; } //提取temperature if(now_object.contains("temperature")) { weather_now.temperature = now_object.take("temperature").toString(); //qDebug() << "temperature : " << temperature; } //提取text if(now_object.contains("text")) { weather_now.text = now_object.take("text").toString(); //qDebug() << "text : " << text; } } } } } } }
将原来的qDebug()打印注释,换成JSON解析函数,然后刷新ui界面上的内容即可。
-
解析代码有了,下面在槽函数中调用解析函数即可
-
-
补充一下
-
如果在运行qt时报错
-
那么是qt的openssl库的问题,以下提供解决方案
如果是PC端
在安装目录
QT\MinGW\Tools\QtCreator\bin
找到libeay32.dll和ssleay32.dll链接库
-
-
复制到QT\MinGW\5.6\mingw49_32\bin目录下,重新编译程序就可以解决
如果是ubuntu端
在安装目录下是找不到这两个库的,所以要重新编译openssl源码
去openssl的官网下载最新的openssl_1.0.2(似乎需要科学上网,也可以到我的github上下载)最好下载最新的版本,我试过1.0版本,但是并不能解决问题。
https://www.openssl.org/source/
下载好之后,放到ubuntu里,解压
tar –zxvf openssl-1.0.2r.tar.gz
cd openssl-1.0.2r
开始编译
./config enable-shared
make depend
make -j4
然后在当前目录生成4个.so的链接库
将这4个链接库,拷贝到qt的编译库下
/opt/Qt5.6.3/5.6.3/gcc_64/lib
然后重新编译运行程序就可以了
如果是ARM板,那么就需要交叉编译,注意修改交叉工具链,然后把生成的链接库拷贝到ARM板的/usr/lib目录下。
-
测试
当然要保证板子能连上外网,才能成功获取数据。如图,测试成功。
Qt完整源码放在github上,有需要可以自行下载。 代码名称为190513.zip和190514.zip
https://github.com/ljy980330/opencv_face_sys
说明
190513.zip只有第一个测试图的界面,190514.zip加入了第二个界面的代码,触发方式为点击第一个界面的天气框内部的任意地方,进入第二个界面,在第二个界面点击屏幕任何区域都可以再次回到第一个界面。
下一章,讲解测试图1中右上角的系统信息如何获取。
有任何问题可以在下面给我留言!大家一起学习!