Qt 5 does not enter in lambda function? - c++

When debug code, never enter into lambda function. Why do I have this problem?
QNetworkRequest req = QNetworkRequest(url);
QNetworkReply *reply = m_manager->get(req);
QObject::connect(reply, &QNetworkReply::finished,[reply](){
qDebug() << "start => ";
if(reply ->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
qDebug() << "response => ";
qDebug() << QString(response);
return response;
} else {
QByteArray error = reply ->readAll();
return error;
}
});

This is the solution to my problem, must create a signal for return data.
QObject::connect(reply,&QNetworkReply::finished,this, [this, reply]() {
qDebug() << "Got reply finished";
if(reply ->error() == QNetworkReply::NoError) {
m_json= reply->readAll();
reply->deleteLater();
emit workResponseChanged(m_workJson);
} else {
m_json= reply ->readAll();
reply->deleteLater();
emit workResponseChanged(m_workJson);
}
});
return QByteArray();

Related

How to measure download percent using QNetwork?

In the example below sometimes the file is downloaded correctly, and sometimes, I'm getting these values in the qDebug() added into the downloadProgress lambda:
Percent complete: -1.08905e+09
Downloaded 11623330 of -1
And then the download fails, I mean it saves a zip file with 0 bytes.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request;
// Random link just to test:
request.setUrl(
QUrl("https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n5.0.2.zip"));
QNetworkReply *reply = manager->get(request);
connect(reply, &QNetworkReply::downloadProgress,
[this, reply](qint64 bytesReceived, qint64 bytesTotal)
{
qDebug() << "Downloaded " << bytesReceived << " of " << bytesTotal;
double percentComplete = (bytesReceived * 100.0) / bytesTotal;
qDebug() << "Percent complete: " << percentComplete;
});
connect(reply, &QNetworkReply::finished, [this, reply]()
{
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Error: " << reply->errorString();
} else
{
QString fileName = "C:/Users/Raja/Downloads/file.zip";
QFile file(fileName);
if (file.open(QIODevice::WriteOnly))
{
file.write(reply->readAll());
file.close();
qDebug() << "File downloaded successfully";
} else
qDebug() << "Error: Unable to open the file";
}
reply->deleteLater();
});
What i'm missing?
Did you read the documentation?
It says that bytesTotal is -1 if the total size is unknown and
The download is finished when bytesReceived is equal to bytesTotal. At that time, bytesTotal will not be -1.
In other words: That behavior is expected and just means the download is still in progress. This probably happens when the server doesn't send a content-length header. See What's the "Content-Length" field in HTTP header?

Trouble when using QNetworkReply in QT

I'm new in C++ also with QT, my current QT version is 5.5.1.
I have an issue when using connect with QNetworkReply and QNetworkAccessManager. When I try to assign a variable from another file to data fetched from API, it always calls the function first before connecting to the API so that the variable is always empty. Can you guys help me?
void NioshFunc::fetchDataFromAPI(){
QUrl url;
QUrlQuery querystr;
querystr.addQueryItem("$format","json");
url.setScheme("https");
url.setHost("example.com");
url.setPath("/aaaa/dataset");
url.setQuery(querystr);
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("KeyId", "xxx-xxx-xxx");
reply = manager->get(request);
connect(reply, &QNetworkReply::readyRead, this, &NioshFunc::readyRead);
connect(reply, &QNetworkReply::finished, this, &NioshFunc::finished);
}
void NioshFunc::readyRead(){
dataBuffer.append(reply->readAll());
}
void NioshFunc::finished(){
if(reply->error())
{
qDebug() << "ERROR!";
qDebug() << reply->errorString();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
}else{
qDebug() << "SUCCESS";
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
nioshWPDataFromAPI = QJsonDocument::fromJson(dataBuffer);
qDebug() << nioshWPDataFromAPI;
dataBuffer.clear();
}
reply->deleteLater();
}
bool NioshFunc::parse(){
Globals *g = Globals::getHandle();
if (nioshWPDataFromAPI.isEmpty() || nioshWPDataFromAPI.isNull()) return false;
QVariantMap root = nioshWPDataFromAPI.toVariant().toMap();
if (root.isEmpty()) return false;
QVariantMap d = root["d"].toMap();
if (d.isEmpty()) return false;
QVariantList results = d["results"].toList();
if (results.isEmpty()) return false;
foreach (QVariant varResult, results)
{
QVariantMap result = varResult.toMap();
if (result.isEmpty()) return false;
g->struct_tableColumn.input.duration = result["Duration"].toString();
g->struct_tableColumn.input.id = result["Id"].toString();
if (id.isEmpty() || id.isNull()) return false;
g->struct_tableColumn.input.load_cumul_Limit = result["LoadCumulLimit"].toString();
g->struct_tableColumn.input.Notes = result["Notes"].toString();
qDebug() << g->struct_tableColumn.input.id << g->struct_tableColumn.input.duration << g->struct_tableColumn.input.load_cumul_Limit << g->struct_tableColumn.input.Notes; // "it is empty for all variable from globals file.
}
return true;
}
When I can function fetchDataFromAPI() and call function parse() in another file named globals, that is always called 2 these functions first before connecting to API, so that the variable named "nioshWPDataFromAPI" is always empty. But when I read nioshWPDataFromAPI in function finished(), it still has data, but only in function finished(). Could you guys help me? Sorry if my question is not too clear for you guys

How to get data out of readyReadSlot?

I am trying to get data out of slot with a signal readyRead(). But my method doesn't seem to work. I googled a lot but still I can't solve the problem.
Here what I have:
In my main function I call the method sendPOST() to get cookies. I got cookies from this method using inside of it SIGNAL finished(QNetworkReply *) and SLOT replyFinishedSlot_(QNetworkReply *) :
connect(manager_, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinishedSlot_(QNetworkReply *)));
I made a public static bool variable isFinished = false by default to write if slot is finished it's job.
replyFinishedSlot_(QNetworkReply ):
if(reply->error())
qDebug() << "Error: " << reply->errorString();
else
{
cookie = reply->manager()->cookieJar()->cookiesForUrl(webReportsUrl);
QString cookieString = cookie[0].name() + "=" + cookie[0].value() + "; domain=" + cookie[0].domain() + "; path=" + cookie[0].path() + ";";
if(reply->isFinished()) isFinished = true; //isFinished is static public variable
}
reply->deleteLater();
And then I check in my main function if isFinished is true, and if it is I connect to another slot:
manager_ = new QNetworkAccessManager(this);
sendPOST("http://url");
if(isFinished)
{
QNetworkAccessManager *man = new QNetworkAccessManager();
QNetworkRequest request(webReportsUrl);
request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookie));
getReply = man->get(request);
connect(getReply, SIGNAL(readyRead()), this, SLOT(readyReadSlot_()));
if(isRead)
qDebug() << "reading";
else qDebug() << "not reading";
}
and isFinished in here works very well (but I am not sure if this is the right way to check finished or not like this). I get isFinished == true, and I can get cookies from replyFinishedSlot_.
But the problem is to get data from readyReadSlot_(). I tried different ways to receive the data from this slot, but there's no successful result.
I tried even something like this:
QEventLoop loop;
connect(getReply, SIGNAL(readyRead()), &loop, SLOT(readyReadSlot_()));
loop.exec();
But I got the error:
QObject::connect: No such slot QEventLoop::readyReadSlot_() in ...
Inside readyReadSlot_() I have to receive all the data from the page:
if(getReply->isReadable())
{
if(getReply->error() != QNetworkReply::NoError)
{
qDebug() << "Error: " << getReply->errorString();
}
else {
isRead = true;
response = getReply->readAll(); //here the data I need outside of this slot
qDebug() << "response: " << response;
}
}
getReply->deleteLater();
And I get it successfully inside, but I need to get response outside of this slot, in my main function, for example.
I know here's something with a threads, and I just don't wait till the data recieved, but I don't know how can I fix it.
I found a problem solvation for me.
void DataMartsModel::replyFinishedSlot_(QNetworkReply *reply)
{
static bool isRead = false;
if(reply->error())
qDebug() << "Error: " << reply->errorString();
else
{
cookie = reply->manager()->cookieJar()->cookiesForUrl(webReportsUrl);
QString cookieString = cookie[0].name() + "=" + cookie[0].value() + "; domain=" + cookie[0].domain() + "; path=" + cookie[0].path() + ";";
QNetworkAccessManager *man = new QNetworkAccessManager();
QNetworkRequest request(webReportsUrl);
request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookie));
getReply = man->get(request);
connect(getReply, &QNetworkReply::readyRead, [=](){
if(getReply->isReadable())
{
if(getReply->error() != QNetworkReply::NoError) qDebug() << "Error: " << getReply->errorString();
else {
isRead = true;
}
}
});
if(reply->isFinished() && getReply->isReadable()) isFinished = true; //here is the problem solvation I wanted
}
reply->deleteLater();
}
main function
manager_ = new QNetworkAccessManager(this);
sendPOST("http://url");
if(isFinished)
{
QByteArray array = getReply->readAll(); //here I got the data I needed to get from readyReady
qDebug() << array; //here I display it and I can use them in the future
}
If you know better way to solve the problem, I would like to check it, too.

QtCUrl post doesn´t work anymore (Linux nok...windows ok)

I have been running this function since last year (Linux and Windows) within my program.
Now I need to implement a new function and my new build is no longer running.
I have other CUrl functions using POST and the results are the same: nok, but my GET functions are ok.
I have another computer (with Mint 19) where this program is running smoothly, but on my computer (using Mint 19, too) the compilation is fine, but it starts curl.exec (I'm using Qtcurl library and inside has a call to curl_easy_perform) and no longer returns.
I have this package installed: libcurl4-openssl-dev
It's okay to compile my program (Linux and Windows). This program is running on Windows.
My problem is just new builds in Mint19.
What is missing to install?
QUrl url("https://pos-api.ifood.com.br/oauth/token");
QUrlQuery q;
q.addQueryItem("client_id", id);
q.addQueryItem("client_secret", secret);
q.addQueryItem("grant_type","password");
q.addQueryItem("username",user);
q.addQueryItem("password",password);
url.setQuery(q);
QtCUrl::Options opt;
opt[CURLOPT_URL] = url;
opt[CURLOPT_POST] = true;
opt[CURLOPT_FOLLOWLOCATION] = true;
opt[CURLOPT_FAILONERROR] = true;
opt[CURLOPT_SSL_VERIFYPEER]= false; // windows
QStringList headers;
headers
<< "cache-control: no-cache"
<< "content-type: application/x-www-form-urlencoded";
opt[CURLOPT_HTTPHEADER] = headers;
val = cUrl.exec(opt); // PROBLEM HERE!!!!
if (cUrl.lastError().isOk()) {
bool ok;
// json is a QString containing the JSON data
QtJson::JsonObject result = QtJson::parse(val, ok).toMap();
token=result["access_token"].toString();
return token;
}
else {
return "";
}
I changed all my methods.
The first funcion is a POST with query.
QString iFood_getToken2(QString token, int *expira, QString id, QString secret, QString user, QString password, QString host){
if(host!=hostname || !ifood_ativo){
qDebug() << "iFood_getToken2 saindo...";
return "";
}
if(*expira>IFOOD_TASK){
*expira-=IFOOD_TASK;
// qDebug() << "expira " << *expira;
return token; // token válido
}
QUrl url("https://pos-api.ifood.com.br/oauth/token");
QUrlQuery q;
q.addQueryItem("client_id", id);
q.addQueryItem("client_secret", secret);
q.addQueryItem("grant_type","password");
q.addQueryItem("username",user);
q.addQueryItem("password",password);
url.setQuery(q);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QVariant(int(QNetworkRequest::AlwaysNetwork)));
QJsonObject json;
QNetworkAccessManager nam;
QNetworkReply *reply = nam.post(request, QJsonDocument(json).toJson());
while (!reply->isFinished())
{
qApp->processEvents();
}
QByteArray response_data = reply->readAll();
QJsonDocument jsonr = QJsonDocument::fromJson(response_data);
reply->deleteLater();
//qDebug() << "ifoodtoken2 " << jsonr["access_token"].toString();
return jsonr["access_token"].toString();
}
I did implement these new functions:
There is a new implementation for a GET and PATCH
So, from now on I dont need to use CUrl library anymore
QJsonDocument networkGet(QString strUrl, QString token){
QUrl url(strUrl);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QVariant(int(QNetworkRequest::AlwaysNetwork)));
QString headerData = "bearer " + token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
QJsonObject json;
QNetworkAccessManager nam;
QNetworkReply *reply = nam.get(request);
while (!reply->isFinished())
{
qApp->processEvents();
}
QByteArray response_data = reply->readAll();
QJsonDocument json_response = QJsonDocument::fromJson(response_data);
reply->deleteLater();
//qDebug() << "networkGet " << json_response << reply->errorString() << headerData ;
return json_response;
}
int networkPatch(QString strUrl, QString token, QJsonDocument json){
QUrl url(strUrl);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QVariant(int(QNetworkRequest::AlwaysNetwork)));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QString headerData = "bearer " + token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
QNetworkAccessManager nam;
QByteArray * _b_arr = new QByteArray (QString(json.toJson()).toLatin1());
QBuffer *_qbf_upload =new QBuffer (_b_arr);
QNetworkReply *reply = nam.sendCustomRequest(request,"PATCH",_qbf_upload);
while (!reply->isFinished())
{
qApp->processEvents();
}
QByteArray response_data = reply->readAll();
QJsonDocument json_response = QJsonDocument::fromJson(response_data);
reply->deleteLater();
qDebug() << "networkPatch " << reply->error() << json_response << reply->errorString() << headerData ;
return reply->error();
}

Qt QNetworkReply is always empty

I want to see the results of a GET request. By my understanding, this code should do it. What am I doing wrong?
void getDoc::on_pushButton_2_clicked()
{
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://www.google.com")));
}
void getDoc::replyFinished(QNetworkReply *reply)
{
qDebug() << reply->error(); //prints 0. So it worked. Yay!
QByteArray data=reply->readAll();
qDebug() << data; // This is blank / empty
QString str(data);
qDebug() << "Contents of the reply: ";
qDebug() << str; //this is blank or does not print.
}
The code compiles and runs fine. It just doesn't work.
Try modifying your replyFinished slot to look like this:
QByteArray bytes = reply->readAll();
QString str = QString::fromUtf8(bytes.data(), bytes.size());
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
You can then print the statusCode to see if you are getting a 200 response:
qDebug() << QVariant(statusCode).toString();
If you are getting a 302 response, you are getting a status redirect. You will need to handle it like this:
if(statusCode == 302)
{
QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "redirected from " + replyUrl + " to " + newUrl.toString();
QNetworkRequest newRequest(newUrl);
manager->get(newRequest);
return;
}
I'm returning when encountering a status code of 302 since I don't want the rest of the method to execute.
I hope this helps!