文章目录
0 背景
因为登陆账号和发送一些数据需要要用到http请求去发送和接受JSON数据,所以查阅了相关资料,加上自己的实践,整理出如下博文。
1 发送数据
1.1 发送和接受数据调用的js方法:
// GET
function get(url, success, failure)
{
var xhr = new XMLHttpRequest;
xhr.open("GET", url);
xhr.onreadystatechange = function() {
handleResponse(xhr, success, failure);
}
xhr.send();
}
// POST
function post(success, failure, url, arg,)
{
var xhr = new XMLHttpRequest;
xhr.open("POST", url);
xhr.setRequestHeader("Content-Length", arg.length);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); //post必备
xhr.onreadystatechange = function() {
handleResponse(xhr, success, failure);
}
xhr.send(arg);
}
// 处理返回值
function handleResponse(xhr, success, failure){
// console.log(xhr);
// console.log(success);
// console.log(failure);
if (xhr.readyState == XMLHttpRequest.DONE) {
if (xhr.status == 200){
if (success != null && success != undefined)
{
var result = xhr.responseText;
try{
success(result, JSON.parse(result));
}catch(e){
success(result, {});
}
}
}
else{
if (failure != null && failure != undefined)
failure(xhr.responseText, xhr.status);
}
}
}
1.2 qml(用于调用js方法)
import QtQuick 2.0
import QtQuick.Window 2.2
import "ajax.js" as Ajax
Item {
//loginAccount
//登录接口
function loginAccount(ajax, deviceid, lastloginversion, password, username, loginfield, type) {
//打印参数
console.log("登录发送的参数:", ajax, deviceid, lastloginversion, password, username, loginfield, type) //打印参数数据
Ajax.post(
function (data){ //成功后的回调函数
console.log("登录成功的返回值:", data);
//成功后调用的槽函数:public属性
$Login.activeResult(data);
console.log('进入登录接口');
},function (data){ //失败后的回调函数
//失败后调用的槽函数
$Login.failHttpResult(data);
console.log(data)
},
"请求的网址:如http://88.888.88.88/api/app/login?",
"ajax=" + ajax
+ "&deviceid=" + deviceid
+ "&lastloginversion="+ lastloginversion
+ "&password=" + password
+ "&username=" + username
+ "&loginfield=" + loginfield
+ "&type=" + type,
Login.invokeFunc
);
}
//发送json结构体
function sendDiscernData2(dataStruct, user_id) {
//imageData,
console.log("发送到游戏端的数据:", dataStruct, user_id) //打印参数数据
Ajax.post(
function (data){ //成功后的回调函数
//成功调用的槽函数
$ShowImage.activeResult2(data);
console.log("识别接口2 success:", data);
},function (){ //失败后的回调函数
//失败调用的槽函数
$ShowImage.failResult2(data);
console.log(" 识别接口2 error:", data);
},
"请求的网址",
"data="+dataStruct
+"&user_id="+user_id,
ShowImage.invokeFunc
);
}
}
以上两个文件作为资源文件引入到项目中【一般qml和调用的js放在相同的目录下】:
1.3 调用方法
头文件.h中
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include<QQmlComponent>
#include<QJsonObject>
#include<QJsonArray>
#include<QJsonValue>
#include<QJsonParseError>
//调登录接口
private:
QQmlApplicationEngine engine;
QObject *engineObject; //指向运行的qml对象
public slots:
/**
* @brief 激活结果回来(可能成功,可能失败)
* @param 返回json数组
* @return 返回是否登录成功
*/
void activeResult(const QVariant &);
/**
* @brief failHttpResult 激活结果失败
*/
void failHttpResult(const QVariant &);
.cpp文件中:
构造函数中加入如下代码:
//设置qml全局访问属性
//engine.load("D:/Qfile/testAjax/file/main.qml");
engine.rootContext()->setContextProperty("Login", this);
//将QML中的Widget变量指向为当前类.从而使QML和widget类连接起来
//创建qml并获取运行中的qml对象
//QStringLiteral 宏可以在编译期把代码里的常量字符串 str 直接构造为 QString 对象
//QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
//QQmlComponent component(&engine, "D:/Qfile/testAjax/file/main.qml");
QQmlComponent component(&engine);
component.loadUrl(QUrl(QStringLiteral("qrc:/http/main.qml")));
engineObject = component.create();
//将引用传递过去给qml进行绑定
engine.rootContext()->setContextProperty("$Login", this);
1.4 处理JSON返回值
调用后返回的值:
{
"success":true,
"jwt":"gfsdgfgs",
"message":{"gender":0,"nation":"","birth_date":"","bio":"","msn":"","real_name":"","type":1,"devicetype":0,"password":"123455","jihuo_status":0,"server_ip":"47.108.50.146","email":"","onlineflag":0,"qq":"","salt":"","mobile":"","avatar":"","priority":0, "deviceid":"1","weixin":"","wifi_name":0,"user_id":1,"nick_name":"","wifi_password":0,"node_id":0,"username":"A1234500"},
"devices":[{"top":28,"mj_count":108,"user_id":1,"bottom":28,"serial_number":"12345601","version_number":1,"direction":0},{"top":26,"mj_count":108,"user_id":1,"bottom":26,"serial_number":"12345602","version_number":1,"direction":1},{"top":28,"mj_count":108,"user_id":1,"bottom":28,"serial_number":"12345603","version_number":1,"direction":2},{"top":26,"mj_count":108,"user_id":1,"bottom":26,"serial_number":"12345604","version_number":1,"direction":3}]
}
调试信息打印出的图片:
解析方法:
//获得返回 的值
void Login::activeResult(const QVariant &var)
{
QVector<QPair<QString, int>>serialNumberInformation;
QMap<QString, int> serialNumber2Direction;
QMap<int, QString> direction2SerialNumber;
QByteArray userAccount;
QString wifiAccount;
QString wifiPassword;
int userId = 0;
QString serial_number;
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(var.toByteArray(), &jsonError);
qDebug()<<"解析文档";
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) {
qDebug()<<"解析未发生错误";
if (doucment.isObject()) {
qDebug()<<"JSON 文档为对象";
QJsonObject object = doucment.object(); // 转化为对象
/*
* 解析单个数据
*/
//登录成功判断
if (object.contains("success")) { // 包含指定的 key
QJsonValue value = object.value("success"); // 获取指定 key 对应的 value
if(value == true){
loginStatus = true;
qDebug()<<"登录成功";
}else{
loginStatus = false;
qDebug()<<"登录失败";
}
}
/*
* 解析多个结构体
*/
if (object.contains("devices")) {
QJsonValue value = object.take("devices");
if(value.isArray()){
QJsonArray arr = value.toArray();
qDebug() << arr.size();
for(int i = 0; i < arr.size(); i++)
{
QJsonValue value = arr.at(i);
QPair<QString, int> tempInfo;
tempInfo.first = value["serial_number"].toString();
tempInfo.second = value["direction"].toInt();
serialNumberInformation.push_back(tempInfo);
serialNumber2Direction[tempInfo.first] = tempInfo.second;
direction2SerialNumber[tempInfo.second] = tempInfo.first;
if(userId == 0){
userId = value["user_id"].toInt();
}
//qDebug()<<i<<":"<<value["serial_number"].toString();
}
}
}//获得序列号
/*
* 解析单个
*/
//获得账号
if(object.contains("message")){
QJsonValue value = object.take("message");
QString tempUsername = value["username"].toString();
qDebug()<<"tempUsername:"<<tempUsername;
for (int i = 0;i < tempUsername.size();i += 2) {
userAccount += HexStringToByteArray(tempUsername.mid(i,2));
}
// qDebug()<<"userAccount:"<<userAccount.toHex().toUpper();
wifiAccount = value["wifi_name"].toString();
qDebug()<<"wifiAccount:"<<wifiAccount;
wifiPassword = value["wifi_password"].toString();
qDebug()<<"wifiPassword:"<<wifiPassword;
}
}// JSON 文档为对象
}// 解析未发生错误
}
2 发送数据
2.1 发送单个数据
m_username = ui->accountComboBox->currentText() ;
m_password = ui->passwordLineEdit->text();
//正式发送数据
QVariant ajax = true;
QVariant deviceid = 1.0;
QVariant lastloginversion = 1.0;
QVariant devicetype = 0;
QVariant password = static_cast<QVariant>(m_password);
QVariant username = static_cast<QVariant>(m_username);
QVariant loginfield = "username";
QVariant type = "username";
//调用方法 发送数据
//发送顺序要和qml传入的顺序一致
QMetaObject::invokeMethod(engineObject, "loginAccount",
Q_ARG(QVariant, ajax),
Q_ARG(QVariant, deviceid),
Q_ARG(QVariant, lastloginversion),
Q_ARG(QVariant, password),
Q_ARG(QVariant, username),
Q_ARG(QVariant, loginfield),
Q_ARG(QVariant, type));
2.2 发送结构体
思路:
- 1 使用QVariantList存储QVariantMap数据
- 2 把QVariantList转为 QJsonArray,再转为QJsonDocument,再转为QString
- 3 去除转后的空格和换行
- 4 调用方法发送数据
void ShowImage::sendAllDiscernCardResult()
{
//获得时间;2020-08-27 18:56:06
QDateTime curDateTime = QDateTime::currentDateTime();
QVariantList varList;
QVariantMap tempMap;
for (int i = 1;i <= eastCardSum;i++) {
tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
tempMap["chupai_direction"] = "1";
tempMap["direction"] = 0;
tempMap["id"] = 0;
tempMap["mahjong_number"] = i;//((i % 2 == 0) ? (i - 1): (i + 1))
tempMap["napai_direction"] = 1;
tempMap["result"] = recognitionCardData[0][0][i];
tempMap["serial_number"] = usrId;
tempMap["yuce_direction"] = 1;
tempMap["zhuo_id"] = 1;
varList<<tempMap;
}
for (int i = 1;i <= southCardSum;i++) {
tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
tempMap["chupai_direction"] = "1";
tempMap["direction"] = 1;
tempMap["id"] = 0;
tempMap["mahjong_number"] = i;
tempMap["napai_direction"] = 1;
tempMap["result"] = recognitionCardData[0][1][i];
tempMap["serial_number"] = usrId;
tempMap["yuce_direction"] = 1;
tempMap["zhuo_id"] = 1;
varList<<tempMap;
}
for (int i = 1;i <= westCardSum;i++) {
tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
tempMap["chupai_direction"] = "1";
tempMap["direction"] = 2;
tempMap["id"] = 0;
tempMap["mahjong_number"] = i;
tempMap["napai_direction"] = 1;
tempMap["result"] = recognitionCardData[0][2][i];
tempMap["serial_number"] = usrId;
tempMap["yuce_direction"] = 1;
tempMap["zhuo_id"] = 1;
varList<<tempMap;
}
for (int i = 1;i <= northCardSum;i++) {
tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
tempMap["chupai_direction"] = "1";
tempMap["direction"] = 3;
tempMap["id"] = 0;
tempMap["mahjong_number"] = i;
tempMap["napai_direction"] = 1;
tempMap["result"] = recognitionCardData[0][3][i];
tempMap["serial_number"] = usrId;
tempMap["yuce_direction"] = 1;
tempMap["zhuo_id"] = 1;
varList<<tempMap;
}
QJsonArray dataArray = QJsonArray::fromVariantList(varList);
// qDebug()<<"dataArray:"<<dataArray;
QJsonDocument dataDoc;
dataDoc.setArray(dataArray);
//
QString dataString = static_cast<QString>(dataDoc.toJson());
//去换行
dataString.remove(QChar('\n'), Qt::CaseInsensitive);
//去空格
dataString.remove(QChar('\040'), Qt::CaseInsensitive);
QVariant dataStruct = static_cast<QVariant>(dataString);
usrId = 1;
QVariant user_id = static_cast<QVariant>(usrId);
//发送数据
QMetaObject::invokeMethod(engineObject, "sendDiscernData2",
Q_ARG(QVariant, dataStruct),
Q_ARG(QVariant, user_id));
}
3 打包时,容易出现的错误
一般qt程序打包,很多人喜欢使用windeployqt 程序路径,但是这样打包出来的程序,虽然release的版本不会报错(qt会到默认的路径下去寻找qml环境文件),但是运行程序却运行不起来,原因在于qml的环境没有打包好(一般),容易出现诸如下面的错误:
解决的方法是:使用如下的命令:
windeployqt.exe --qmldir ./qml 程序路径