Qt cpp cant send obj to connected function - c++

I need to get a json file from an url, fill it in a QtNetworkReply *reply and send reply in a connected fonction to convert it in QbyteArray to pars my Json response.
But when i go in my connected function, i cant fill QByteArray with that reply (always empty)
Here's my code :
int main(int ac, char *av[])
{
Borne borne(ac, av);
reply myReply;
QNetworkAccessManager networkManager;
QUrl url("http://vps202498.ovh.net:8080/ws/rest/v.1/stores/categories/150/products");
QNetworkRequest request;
request.setUrl(url);
myReply._reply = networkManager.get(request);
QObject::connect(myReply._reply, SIGNAL(finished()), &myReply, SLOT(fonction()));
myReply._reply->finished();
exit(1);
if (borne.initialize() == false)
return (false);
return (borne._app->exec());
}
And here's my connected function :
IProduct *reply::fonction()
{
QByteArray List;
std::cout << "connected" << std::endl;
List = _reply->readAll();
if (List.isNull())
exit(6);
return (NULL);
}
My .H :
class reply : public QObject
{
Q_OBJECT
public:
reply() {};
~reply() {};
QNetworkReply *_reply;
public slots:
IProduct *fonction();
private :
};
I cant std::cout "connected", but always quit with error log '6'.
I dont really know where am i doing mistake (Iam used to C, not Cpp), i've read all the man of Qt about it, and cant figure what going wrong.
Any ideas?
Thank you and apologize for weak skill and english

You call the finish() function manually immediately after creation of the request. In that moment the request is not even started, so there is nothing to read from _reply->readAll(). The reply finished signal should be called by the even loop after calling application exec().
Remove lines:
myReply._reply->finished();
exit(1);
The request will be processed asyncronously in the event loop.
Other issues:
the slot reply::fonction() does not need any retrun value;
the event loop may be not started because of (borne.initialize() == false).

Related

How to send a REST call with Qt?

I have started started working with Qt framework and after reading the documentation for Qt5 and some examples across some blogs, I wrote the following program but I does not seem to do the correct job.
I am writing a class for which I need to write a method Login and logout.
For login method, I am writing following code:
void User::login()
{
const QUrl loginUrl = (this->m_url).append("/api/auth/login");
QNetworkRequest loginRequest;
loginRequest.setUrl(loginUrl);
loginRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonObject body;
QJsonObject data;
data.insert("userName", this->m_userName);
data.insert("password", this->m_password);
body.insert("data", data);
body.insert("provider", "LDAP");
//loginRequest.setBody(QJsonDocument(body).toJson());
const QByteArray json = QJsonDocument(body).toJson();
QNetworkReply* reply = m_manager.post(loginRequest, QJsonDocument(body).toJson());
while (!reply->isFinished())
{
// wait for the request to complete
}
QByteArray response_data = reply->readAll();
QJsonDocument responseJson = QJsonDocument::fromJson(response_data);
reply->deleteLater();
std::cout << response_data.toStdString() << std::endl;
}
After I call this method in my main function, If I check in fiddler, I cannot see any request made also, the program goes into infinite loop. Can you tell me what is wrong?
Cause
As you say, the program goes into infinite loop. The very one you have created:
while (!reply->isFinished())
{
// wait for the request to complete
}
Simply put, with QNetworkReply* reply = m_manager.post(loginRequest, QJsonDocument(body).toJson()); you do not send a request, but merely declare your wish to do so. Right after that you block the event loop, leaving Qt no chance to fulfill your wish.
Solution
Use signals and slots as intended. Think about team as events and callbacks. A good starting point is the description of QNetworkAccessManager:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished,
this, &MyClass::replyFinished);

How do I read the data from QNetworkReply?

How do I read the data from a QNetworkReply response from a specific URL before QWebPage does? but when the finished() signal is emited the reply is read already by QWebPage, so connect readyRead() or call reply->readAll() return nothing. I tried overload acceptNavigationRequest() method in my own QWebPage class, something like this:
bool webPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, QWebPage::NavigationType type)
{
//qDebug() << "filename = " << request.rawHeader("content-disposition");
if(request.url().path() == QStringLiteral("download.php"))
{
QNetworkReply *reply = networkAccessManager()->get(request);
QFile file;
file.setFileName(filename);
if(!file.open(QIODevice::ReadWrite))
{
/* handle error */
}
file.write(reply->readAll());
file.close();
return false;
}
But I couldn't manage to reply work... the returned reply is invalid (don't even return a http status code, I know it means the http request sent is invalid but i don't know why).
Different approachs to solve this are welcome!
Using the finished slot with a lambda expression, you can do this: -
QNetworkReply* reply = networkAccessManager()->get(request);
connect(reply, &QNetworkReply::finished, [=]() {
if(reply->error() == QNetworkReply::NoError)
{
QByteArray response = reply->readAll();
// do something with the data...
}
else // handle error
{
qDebug(pReply->errorString());
}
});

How to get to the main event loop QCoreApplication within nested loops of other classes for implementing networking operations?

This post is similar to the first one I posted, but I made some changes and I understand what is wrong. The error lies in event loop QCoreApplication that is not called within the nested loops and consequently the networking events are not met. My problem is always to send a json to my API Rest. This is the tour that is my code: the main class that calls the SerialReader laws of bytes from the serial port, identifies if the package is right and if it sends the Parser class that divides the various bytes and then sending those required API
This is my main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
SerialReader serialReader;
QTimer::singleShot(0, &serialReader, SLOT(runSerialReader()));
...
return a.exec();
}
This is my serialreader.cpp / serialreader.h
/*serialreader.h*/
class SerialReader : public QObject
{
Q_OBJECT
...
public:
void processData(QByteArray datas);
public slots:
void runSerialReader();
private:
Parser parser;
...
};
/*serialreader.cpp*/
SerialReader::SerialReader() {}
void SerialReader::runSerialReader(){
...
if (serialPort->isOpen())
{
qDebug() << "Serial port is open...";
while (serialPort->waitForReadyRead(50))
{
QByteArray datas = serialPort->readAll();
if (datas.size() == 0){
qDebug() << "ERROR data not read";
} else {
processData(datas);
}
}
} else {
qDebug() << "OPEN ERROR: " << serialPort->errorString();
}
serialPort->close();
qDebug() << "...serial port is closed!";
...
}
void SerialReader::processData(QByteArray datas)
{
for (int i = 0; i < datas.size(); i++)
{
switch (state)
{
...
case EXIT:
...
parser.setPackage(serialBuffer);
...
}
}
}
This is my parser.cpp / parser.h
/*parser.h*/
class Parser : public QObject
{
Q_OBJECT
public:
...
QQueue<QByteArray> queue;
void setPackage(QByteArray &serialBuffer);
void sendPacketToAPI(QByteArray &bufferToPacket);
public slots:
void replyFinished(QNetworkReply *reply);
private:
QNetworkAccessManager *m_manager;
quint8 m_a;
quint8 m_b;
}
/*parser.cpp*/
Parser::Parser()
: m_manager { new QNetworkAccessManager }
{
connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
}
void Parser::setPackage(QByteArray &serialBuffer){
...
bufferToPacket.append(bufferToParse.at(i));
...
}
void Parser::replyFinished(QNetworkReply *reply)
{
reply->deleteLater();
qDebug() << "~error " << reply->errorString();
qDebug() << "reply delete!";
qDebug() << "https post_request done!";
}
void Parser::sendPacketToAPI(QByteArray &bufferToPacket){
m_a = bufferToPacket.at(0);
m_b = bufferToPacket.at(1);
QNetworkRequest request;
request.setUrl(QUrl ("http://..."));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QString json = QString("{\"a\":\"%1\",\"b\":\"%2\"}").arg(m_a).arg(m_b);
m_manager->post(request, json.toUtf8());
QCoreApplication :: processEvents (QEventLoop :: AllEvents);
}
In practice after entering the serial reader read in continuation of bytes so as to become an infinite loop and are therefore always recalled all nested methods.
I tried to solve the problem with the static method QCoreApplication::processEvents(QEventLoop::AllEvents) but I can not play the main loop QCoreApplication.
How can I do?
Calling QCoreApplication::processEvents does not seem to be a good solution and with little refactoring you won't need it.
Qt gives you two options to handle serial port. You can use either blocking API or async API of QSerialPort. However when using blocking API (as you are doing when calling waitForReadyRead()) you should move code that reads serial data to some worker thread to not block the main event loop. The other option is to use async API and connect to readyRead() signal to process incoming data.
In your case it is probably easier to do modify SerialReader class and replace while(...) loop with asynchronous processing of incoming data in a slot connected to readyRead() signal.
There are good examples for QSerialPort http://doc.qt.io/qt-5/qtserialport-examples.html

How to use QNetworkManager for REST api?

I would like to make a class for accessing data via REST API, for example:
class MeteoStation{
int getLatestTemperature();
int getLatestPessure();
private:
QNetworkManager nmng;
}
How could I implement this methods? Usually I was using something like:
int MeteoStation::getLatestTemperature(){
...
QEventLoop eventLoop;
connect(&m_nam,SIGNAL(finished(QNetworkReply*)),&eventLoop,SLOT(quit()));
QNetworkReply *reply = m_nam.get( req );
eventLoop.exec();
reply->readAll()
...
}
But since using inner QEventLoop is not recommended, how should I see to whom the response belong to?
MeteoStation::MeteoStation(){
connect(&nmam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseNetworkResponse(QNetworkReply*)));
}
void MeteoStation::parseNetworkResponse( QNetworkReply *finished )
{
QByteArray data = finished->readAll();
...
Yes and it would be nice to have the class thread save. How are you solving that in your code?
How bad is making the call synchronous with:
QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));
QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();
I've resolved my problem using QCoreApplication::processEvents(). The response is there within ms and I'm able to implement functionality close to libcurl.
QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));
QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();
The Qt docs should provide all info you need.
You creat a nam, connect the finished signal, send the request.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseNetworkResponse(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
Detecting to which request a reply belongs should not be too hard. The reply contains the url. It might be different, but not that different:
...but for a variety of reasons it can be different (for example, a
file path being made absolute or canonical).
QUrl QNetworkReply::url() const
Returns the URL of the content downloaded or uploaded. Note that the
URL may be different from that of the original request.

Qt C++ Console Server, Wait for socket connection & accept input at same time?

I am writing a server as a Qt console application. I have the server set up to wait for a socket connection, but I also need to allow a user to input commands into the server for managing it. Both are working independently. However, the problem I ran into is that when I'm in a while loop accepting and processing input commands, the server doesn't accept connections.
I have a Socket class, and in its constructor, I have:
connect(server,SIGNAL(newConnection()),this, SLOT(newConnection()));
Right under that in the constructor, I call a function that has a more in-depth version of this for getting commands from the user:
QTextStream qin(stdin, QIODevice::ReadOnly);
QString usrCmd;
while(usrCmd != "exit" && usrCmd != "EXIT") {
//Get command input and process here
}
Inside newConnection(), I just accept the next connection and then use the socket.
QTcpSocket *serverSocket = server->nextPendingConnection();
How can I make it so the socket can wait for connections and wait for user-inputed commands at the same time?
Problem with your code is because you are blocking event loop with your while loop. So, the solution to your problem is to read from stdin asynchronously. On Linux (and on Mac, I guess), you can use QSocketNotifier to notify when the data is arrived on stdin, and to read it manually), as per various internet sources.
As I am using Windows, I would suggest you to do it in this way (which should work on all platforms):
Open the thread for reading data from stdin
Once you get some data (perhaps line?) you can use Qt signal-slot mechanism to pass the data to main thread for processing without blocking the event loop.
So, this is the pseudocode. MainAppClass should your existing server class, just edit the constructor to create new thread, and add new slot for processing the data.
class Reader: public QThread
{
Q_OBJECT
public:
Reader(QObject * parent = 0 ): QThread(parent){}
void run(void)
{
forever{
std::string data;
std::getline (std::cin, data);
if(data == "exit")
{
emit exitServer();
return;
}
emit dataReady(QString::fromStdString(data));
}
}
signals:
void dataReady(QString data);
void exitServer();
};
class MainAppClass: public QObject
{
Q_OBJECT
public:
MainAppClass()
{
Reader * tr = new Reader(this);
connect(tr, SIGNAL(dataReady(QString)), this, SLOT(processData(QString)));
connect(tr, SIGNAL(exitServer()), this, SLOT(exitServer()));
tr->start();
}
public slots:
void processData(QString data)
{
std::cout << "Command: " << data.toStdString() << std::endl;
}
void exitServer()
{
std::cout << "Exiting..." << std::endl;
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainAppClass myapp; //your server
app.exec();
return 0;
}
Since I wrote simple guidelines how to use QTcpSocket, here is the brief
When you get client QTcpSocket, connect readyRead() signal to some slot, and read data from sender() object. You don't need to read anything in the constructor.
For reading you can use standard QIODevice functions.
Note: this is pseudo code, and you may need to change few things (check the state of the stream on reading, save pointer to sockets in some list, subscribe to disconnected() signal, call listen() in constructor, check if QTcpServer is listening, etc).
So, you need to have slot onReadyRead() in your class which will have the following code:
void Server::readyReadSlot()
{
QTcpSocket *client = (QTcpSocket*)sender(); // get socket which emited the signal
while(client->canReadLine()) // read all lines!
// If there is not any lines received (you may not always receive
// whole line as TCP is stream based protocol),
// you will not leave data in the buffer for later processing.
{
QString line = client->readLine();
processLine(line); // or emit new signal if you like
}
}
Inside newConnection() you need to connect readyRead() signal with your slot.
void Server::newConnection()
{
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
}