这个项目的最终目的是实现在ARM板上,使用qt+opencv实现人脸识别的考勤系统。上一次完成了打开摄像头代码,这一次我打算在系统中加入通过网络实时获取当地天气情况的功能。
在这里我选择使用心知天气提供的API接口来完成这次任务。JSON格式。
心知天气的官网网址
https://www.seniverse.com
注册登录后,进入控制台,点击我的API项目
记录好自己的API私钥,这里被和谐了,因为私钥是重要的访问密码,最好不要透露出去。
因为是免费用户,所以只有3个API接口可以调用,其他的都不能用,访问频率也被限制到400次/小时,或者20次/分钟。(当然如果你是土豪可以考虑花钱购买)超过了访问频率,服务器将会拒绝你的访问,直到一个小时后再恢复。
然后点击产品文档查看API格式
点击天气现象代码说明这里,下载官方提供的图标,程序中会用到。至于使用哪个图标就看你自己喜好了。
接下来看API调用格式,因为是免费用户,可以使用的三个API分别是天气实况,逐日天气和生活指数。
第一个,天气实况
将这个url地址复制下来,将key=后面的打码部分改为自己的私钥,location=后面的beijing改为ip,其他的不用改。ip的意思是自动根据IP地址定位城市,如果想查询指定的城市,可以通过上面的“接口中的通用参数”了解,这一章对API中的参数设置有详细说明。JSON格式我就不细说了,自己去了解吧。
第二个,逐日天气
把url地址复制下来,参数修改如上。
第三个,生活指数
url地址复制下来。
API接口记录完毕,接下里开始写代码。
访问网址用到了QT的两个类,QNetworkAccessManager和QNetworkReply
记得包含头文件和在.pro文件中加入network
#include
#include
#include
#include
在.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格式中解析出来。
QT5版本以后提供了JSON解析的库,我们可以直接通过QT进行调用。
加入头文件
#include
#include
#include
#include
这里为了方便数据处理,自定义了三个结构体进行管理
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库的问题,以下提供解决方案
在安装目录
QT\MinGW\Tools\QtCreator\bin
找到libeay32.dll和ssleay32.dll链接库
复制到QT\MinGW\5.6\mingw49_32\bin目录下,重新编译程序就可以解决
在安装目录下是找不到这两个库的,所以要重新编译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中右上角的系统信息如何获取。
有任何问题可以在下面给我留言!大家一起学习!