QT QNetworkAccessManager and Signals - c++

I have a class (RequestHandler) that I call from my main Application class to call various web services. Once a service has completed the class sends a Signal back to the calling class to say that it has finished(possibly with QString message). I have the Signal working in a test method but the QNetworkAccessManager is not working at all. I am quite new to QT and C++. Thanks for your help.
// RequestHandler.h
#ifndef REQUESTHANDLER_H_
#define REQUESTHANDLER_H_
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QOBJECT>
#include "MainAppClass.hpp"
class RequestHandler : public QObject
{
Q_OBJECT
public:
explicit RequestHandler(QObject *parent = 0);
void validateRegistration(QString reg);
void onStatusUpdateCompleted();
void sayHi();
signals:
void sendSignal(QString txt);
private:
QNetworkAccessManager* manager;
QObject* thisObj;
public slots:
void onRequestCompleted();
};
#endif /* REQUESTHANDLER_H_ */
RequestHandler.cpp
#include "RequestHandler.h"
RequestHandler::RequestHandler(QObject *parent) : QObject(parent)
{
thisObj= parent;
}
void RequestHandler::validateRegistration(QString reg) {
QNetworkRequest request;
request.setUrl(QUrl("the_registration_url"));
manager = new QNetworkAccessManager();
QNetworkReply *reply = manager->get(request);
connect(reply, SIGNAL(finished()), this, SLOT(onRequestCompleted()));
}
void RequestHandler::onRequestCompleted() {
// not getting here at all
}
void RequestHandler::sayHi()
{
// this is working
QObject::connect(this, SIGNAL(sendSignal(QString)), thisCellCast, SLOT(recieveValue(QString)));
emit sendSignal("HERES THE SIGNAL");
}
I am calling this class like this:
// test slots and reg
RequestHandler rh(this);
//working
rh.sayHi();
// not working
rh.validateRegistration("test");
Thanks for your help.

You typically should be using QNetworkAccessManager by connecting to error() and
finished() signals, so that you will be notified on error occurred.
This is really bad idea to construct QNetworkAccessManager object in validateRegistration() as it will cause memory leak and you need only one object of such
kind. So do it in RequestHandler's constructor.
void RequestHandler::validateRegistration(QString reg)
{
QUrl url("the_registration_url");
QNetworkRequest request(url);
QNetworkReply *reply = manager->get(request);
connect(reply, SIGNAL(finished()), SLOT(onRequestCompleted()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
SLOT(onError(QNetworkReply::NetworkError)));
}
void RequestHandler::onRequestCompleted()
{
qDebug() << "Request succeeded";
}
void RequestHandler::onError(QNetworkReply::NetworkError code)
{
qError() << "Request failed with code " << code;
}
And also make sure you have QApplication::exec() called somewhere, so you have the main event loop running.

Related

Qt C++ QNetworkRequest not making any requests

I'm trying to fetch some data from an API using QNetworkRequest following this video (https://youtu.be/G06jT3X3H9E)
I have a RoR server running on localhost:3000 and I'm trying to fetch something from it.
.h file:
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QAuthenticator>
#include <QNetworkProxy>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
signals:
public slots:
void get(QString code);
private slots:
void readyRead();
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
void encrypted(QNetworkReply *reply);
void finished(QNetworkReply *reply);
void networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
void preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator);
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
private:
QNetworkAccessManager manager;
};
#endif // WORKER_H
.cpp file:
void Worker::get(QString code)
{
qInfo() << "Getting something from the server...";
QNetworkReply* reply = manager.get(QNetworkRequest(QUrl(QString("http://localhost:3000/api/v1/circle_gift_cards/scan?codes=" + code))));
connect(reply, &QNetworkReply::readyRead, this, &Worker::readyRead);
}
void Worker::readyRead()
{
qInfo() << "ReadReady";
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if(reply) qInfo() << reply->readAll();
}
The result from:
Worker worker;
worker.get("abc");
is: "Getting something from the server..."
it should print "ReadReady" when the request is ready, but I don't think the request is being made, there's nothing in the console either.
[Edit 1]
What is calling the worker is this
void MainWindow::on_lineEditScanCard_textChanged(QString code) {
Worker worker(this->site);
worker.get(code);
}
It's an Edit field(where the user is supposed to type a code
[Edit 2]
I removed all app code and did this:
#include <QApplication>
#include "Forms/mainwindow.h"
#include "Requests/worker.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Worker worker;
worker.get("abc");
return a.exec();
}
And it worked... Why it does not work when called when the Edit text is changed?
Remember the concepts of scope, life cycle, and local variables? In your case worker is a local variable that will be destroyed instantly so the slot is not invoked, use
Worker * worker = new Worker;
worker->get("abc"); //remember to delete the memory when you no longer use it

Can't connect SIGNAL to SLOT in same classe and also two differents classes QT

I am developing an QT application under QT5.13, and I am trying to connect a signal with a slot of the same class because the end goal was to connect the signal of this class with the slot of a seconde class but it doesn't work in both cases and so I tried to validate it in the same class before going to the second step.
There is my code :
udpserver.h :
#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <QObject>
#include <QUdpSocket>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
class UDPServer : public QObject
{
Q_OBJECT
public:
explicit UDPServer(QObject *parent = nullptr);
QUdpSocket *getSocket() const;
void Send(QString d);
void pMsg(QByteArray App_Msg);
signals:
void ack_gui(QString ack_msg);
public slots:
void readyRead();
void ackRead(QString _ack_msg);
private:
QUdpSocket *socket;
};
#endif // UDPSERVER_H
udpserver.cpp:
#include "udpserver.h"
UDPServer::UDPServer(QObject *parent) : QObject(parent)
{
socket = new QUdpSocket(this);
QTextStream(stdout) << "Socket Server created ! " << endl;
socket->bind(QHostAddress::LocalHost, QT_SERVER_PORT);
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(socket, SIGNAL(ack_gui(QString)), this, SLOT(ackRead(QString)));
}
QUdpSocket *UDPServer::getSocket() const
{
return socket;
}
void UDPServer::SendData(QString d)
{
keyprod prod1;
QByteArray Data;
QJsonObject Js_command = prod1.ObjectFromString(d);
Data.append(d);
socket->writeDatagram(Data, QHostAddress::LocalHost, PYTHON_SERVER_PORT);
qDebug() << "catch! " << endl;
}
void UDPServer::pMsg(QByteArray App_Msg)
{
QJsonDocument JsonDocument = QJsonDocument::fromJson(App_Msg);
QJsonObject JsonApp_Msg = JsonDocument.object();
QString Typo = JsonApp_Msg["no"].toString();
emit ack_gui(Typo);
}
void UDPServer::readyRead()
{
QByteArray buffer;
buffer.resize(socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
socket->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
pMsg(buffer);
}
void UDPServer::ackRead(QString _ack_msg)
{
qDebug() << "Message : " << _ack_msg;
}
As you can see it is a very classic class nothing very complicated in QT,the first connects works, but the second where I use my personal signal does not work :
connect(socket, SIGNAL(ack_gui(QString)), this, SLOT(ackRead(QString)));
The code are compiled, but when i lunch it, i got this :
QObject::connect: No such signal QUdpSocket::ack_gui(QString) in ../../udpserver.cpp
I also tried the new connect syntax in QT5 but it didn't work.
I know this topic has been touched on several times already but i have already checked these topics and still haven't found a solution can you help me please.
Thank you in advance for your help
That's because the ack_gui signal is a member of your UDPServer class not QUdpSocket.
Use the new signal/slot syntax...
connect(this, &UDPServer::ack_gui, this, &UDPServer::ackRead);
Do not use the old signal/slot connection, you will get better compile time warnings with the new one: https://wiki.qt.io/New_Signal_Slot_Syntax
You are connecting QUdpSocket to your UDPServer. Your UdpServer has ack_gui signal but QUdpSocket has not!

When trying to get a webpage using Qt QNetworkAccessManager, It always returns ""

I'm trying to get a web page using Qt and print it on the screen.The problem is it always prints "". It won't make it to the done SLOT. I don't know if there's something wrong with the connect(). The code gets compiled with no errors.
Trying not to use event loops yet.
Here's the code:
net.h
#ifndef NET_H
#define NET_H
#include <QObject>
#include <QtNetwork>
#include <QString>
#include <QDebug>
class net : public QObject
{
Q_OBJECT
public:
explicit net(QObject *parent = 0);
void get_site(QString url);
QString data;
signals:
public slots:
void err(QNetworkReply *);
void done(QNetworkReply *);
private:
};
#endif // NET_H
net.cpp:
#include "net.h"
net::net(QObject *parent) :
QObject(parent)
{
}
void net::get_site(QString url) {
QNetworkAccessManager man;
QNetworkRequest request;
request.setUrl (QUrl(url));
connect (&man , SIGNAL(finished(QNetworkReply*)) ,this, SLOT(done(QNetworkReply*)));
connect (&man , SIGNAL(finished(QNetworkReply*)) ,this, SLOT(err(QNetworkReply *)));
man.get (QNetworkRequest(QUrl(url)));
}
void net::done(QNetworkReply * reply) {
data = QString(reply->readAll ());
}
void net::err(QNetworkReply * reply) {
data = QString(reply->errorString ());
}
And main.cpp:
#include <QCoreApplication>
#include "net.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
net netobj;
netobj.get_site("http://stackoverflow.com");
qDebug() << netobj.data;
return a.exec();
}
There are some major problems in your code. First you should have the QNetworkAccessManager as a class member in .h file :
class net : public QObject
{
...
private:
QNetworkAccessManager nam;
};
Also you should not connect the finished signal to two different slots. Your get_site function should be like :
void net::get_site(QString url) {
QNetworkRequest request;
request.setUrl (QUrl(url));
connect (&man , SIGNAL(finished(QNetworkReply*)) ,this, SLOT(done(QNetworkReply*)));
man.get (QNetworkRequest(QUrl(url)));
}
And you should manage the returned reply in the following way :
void net::done(QNetworkReply * reply) {
if (reply->error() == QNetworkReply::NoError)
{
data = QString(reply->readAll ());
}
else
{
data = QString(reply->errorString ());
}
}
Your QNetworkAccessManager instance goes out of scope at the end of your get_site function. According to Qt docs, one QNetworkAccessManager should be enough for the whole Qt application. Your object should persist outside the scope of that function, since it's likely the response will take longer to receive than it will take for that function to end. Make the QNetworkAccessManager a member of your net class, instead of an automatic variable local to get_site.
Note that you also need to manage the lifetime of the QNetworkReply object, not by using delete, but by using deleteLater() or else you might leak memory.

Qt C++ Slot is not called when Signal is sent

After debugging, I'm sure that the replyFinish() slot is not called when I call this->getNetReply(). These are my files, in the main() fumction I call the getNetReply this way: Networking a; a.getNetReply();
I did add QT+=network to my qmake.
Please help me. Thank you very much.
my networking.cpp file
#include "networking.h"
#include <QUrl>
#include <QNetworkRequest>
// constructor
void Networking::getNetReply(){
QNetworkAccessManager *man = new QNetworkAccessManager(this);
QObject::connect(man, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
qDebug() << "connected";
QNetworkRequest req;
req.setUrl(QUrl("http://www.google.com"));
man->get(req);
}
// this method not called
void Networking::replyFinished(QNetworkReply *reply){
QByteArray data = reply->readAll();
QString str = QString(data);
this->netRep = str;
code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
}
my networking.h header file:
#ifndef NETWORKING_H
#define NETWORKING_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
class Networking : public QObject
{
Q_OBJECT
public:
QString netRep;
int code;
explicit Networking(QObject *parent = 0);
void getNetReply();
public slots:
void replyFinished(QNetworkReply*);
};
#endif // NETWORKING_H
The get() function returns a QNetworkReply object. Connect to the error signal of that object and it will tell you if an error happens (and which).

QNetworkReply emits error signal twice when ContentNotFoundError occures when event loop is started in error slot

Im using QtSDK 4.7.3
I am doing this in (void test()):
mgr = new QNetworkAccessManager();
reply = mgr->get(QNetworkRequest(QUrl("http://developer.qt.nokia.com/fileNotExisting.txt")));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
SLOT(onError(QNetworkReply::NetworkError)), Qt::ConnectionType::UniqueConnection);
And of course the slot onError is called:
if (networkError == QNetworkReply::NetworkError::ContentNotFoundError)
{
// Messagebox starts an event loop which
// causes this slot to be called again
QMessageBox m;
m.exec();
}
If i don't have a messagebox/eventloop in the onError slot there is no crash and everything works. But when it is there then the onError slot gets called again when m.exec() is called.
When both messageboxes are closed and I leave the function onError the application crashes.
The application tries to delete/free memory when this happens. The error "Access violation reading location" does not help any and the call stack is deep in to Qt dlls.
What I have checked:
The signal is not connected twice.
Tried calling test() before and after the QApplication calls it's exec function. (does not matter).
Another error like HostNotFound will not call the onError slot twice.
All my code is executed in the main thread.
Tried disconnecting the onError slot so it is only called once but it still crashes.
Tried calling abort on the request in onError().
Posted the same question on Qt forum (post).
Can anyone help me figure out what is happening here?
Here is the code I use for testing:
main.cpp
#include "contentnotfound.h"
#include <QtGui/QApplication>
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ContentNotFound cnf;
// false: start test after application's event loop have started
if (true) { cnf.test(); }
else { QTimer::singleShot(2000, &cnf, SLOT(test())); }
return a.exec();
}
contentnotfound.h
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMessageBox>
class ContentNotFound : public QObject
{
Q_OBJECT
public slots:
void test()
{
mgr = new QNetworkAccessManager();
reply = mgr->get(QNetworkRequest(QUrl("http://developer.qt.nokia.com/fileNotExisting.txt")));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
SLOT(onError(QNetworkReply::NetworkError)), Qt::ConnectionType::UniqueConnection);
}
private slots:
void onError(QNetworkReply::NetworkError networkError)
{
//reply->disconnect(); // Disconnect all signals
if (networkError == QNetworkReply::NetworkError::ContentNotFoundError)
{
// Messagebox starts an event loop which
// causes this slot to be called again
QMessageBox m;
m.exec();
}
}
private:
QNetworkAccessManager* mgr;
QNetworkReply* reply;
};
There is a bug in Qt < 4.8.0: https://bugreports.qt.io/browse/QTBUG-16333
Modifying the connection with a queued one solves the problem:
contentnotfound.h:
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMessageBox>
class ContentNotFound : public QObject
{
Q_OBJECT
public slots:
void test()
{
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
mgr = new QNetworkAccessManager(this);
reply = mgr->get(QNetworkRequest(QUrl("http://developer.qt.nokia.com/fileNotExisting.txt")));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
SLOT(onError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
}
private slots:
void onError(QNetworkReply::NetworkError networkError)
{
//reply->disconnect(); // Disconnect all signals
if (networkError == QNetworkReply::ContentNotFoundError)
{
// Messagebox starts an event loop which
// causes this slot to be called again
QMessageBox m;
m.exec();
}
}
private:
QNetworkAccessManager* mgr;
QNetworkReply* reply;
};