i have something really strange i have this code :
i think i know what is wrong but i dont know how to fix it .
this is what i have :
when i put break point in int test = 0;
it getting there before it gets to httpFinished() slot in the HttpClient , mybe this is the problem ?
in the main.cpp
---------------------------------------------------------------------------------------------------------
#while (i.hasNext())
{
i.next();
ThreadWorker* pThreadWorker = new ThreadWorker();
pThreadWorker->setUrl(sUrl);
QThreadPool::globalInstance()->start(pThreadWorker);
}
QThreadPool::globalInstance()->waitForDone();
---------------------------------------------------------------------------------------------------------
void ThreadWorker::run()
{
startWork();
}
void ThreadWorker::startWork()
{
m_pHttpClient = new HttpClient();
m_pHttpClient->startRequest(m_url);
int test = 0;
}
--------------------------------- HttpClient based on the http example from Qt -----------------------------------
HttpClient::HttpClient()
{
m_networkManager = new QNetworkAccessManager(this);
connect(m_networkManager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(slotAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
#ifndef QT_NO_OPENSSL
connect(m_networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
}
void HttpClient::startRequest(QUrl url)
{
m_url.setUrl("http://qt.nokia.com/");
QNetworkRequest request;
request.setUrl(m_url);
reply = m_networkManager->get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(finished()),
this, SLOT(httpFinished()));
connect(reply, SIGNAL(readyRead()),
this, SLOT(httpReadyRead()));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(updateDataReadProgress(qint64,qint64)));
}
the httpFinished() function that is under private slots: never triggered , why ?
UPDATED THE QUESTION
Since the HttpClient and QNetworkAccessManager objects are created within the thread, they automatically belongs to that thread (see QObject::moveToThread), and they both needs an event loop running in that thread, for QNAM to do any work at all, and for your QObject derived class to be able to execute the slots.
You could add a call to QThread::exec() in run() to run that event loop (if you were using QThread):
void Thread::run()
{
startWork();
exec();
}
or create and start a QEventLoop whose quit() slot has to be connected somewhere to stop the loop (for example a finished() signal in the class HttpClient that you would emit when the work is done):
void ThreadWorker::run()
{
startWork();
QEventLoop loop;
QObject::connect(m_pHttpClient, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
Also, since Qt 4.8, QNetworkAccessManager is multithreaded, so you might not need to use threads yourself.
Related
I'm writing an embedded RESTful API client using the Qt5 Embedded layer of OpenEmbedded project. I want my client to be able to send a simple async request to notify my server on what's going on and an other scenario is that my client needs to synchronize data from the server.
To do so I wrote two functions, one to send the request and another to get the response. So if I don't care about the response, I only use the first one.
To be able to use the response with the second one, I use a QNetworkReply * as a member of my class. To keep the QNetworkReply alive, I also set the QNetworkAccessManager as a member of my class.
#include <QtNetwork>
class ApiClient : public QObject
{
Q_OBJECT
public:
ApiClient(QObject *parent = 0);
private:
QString host;
QString key;
quint32 replyTimeout;
QNetworkAccessManager manager;
QNetworkReply *reply;
void sendRequest(const QString &method, const QVariantMap ¶ms = QVariantMap());
QVariantMap getResponse() const;
};
The apiclient.cpp file:
#include "apiclient.h"
ApiClient::ApiClient(QObject *parent) : QObject(parent)
{
}
void ApiClient::sendRequest(const QString &method, const QVariantMap ¶ms)
{
QUrl url(QString("%1/%2/").arg(host).arg(method));
QUrlQuery query;
query.addQueryItem("key", key);
if (!params.empty())
{
QMapIterator<QString, QVariant> it(params);
while (it.hasNext())
{
it.next();
query.addQueryItem(it.key(), it.value().toString());
}
}
url.setQuery(query);
qDebug() << "url: " << url.toString();
reply = manager.get(QNetworkRequest(url));
}
QVariantMap ApiClient::getResponse() const
{
if (!reply)
{
qFatal("No request sent!");
}
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
timer.start(replyTimeout);
loop.exec();
if (timer.isActive())
{
timer.stop();
if (reply->error())
{
qFatal("Wrong reply!");
}
int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (code != 200)
{
qFatal("Invalid server response!");
}
QJsonDocument result = QJsonDocument::fromJson(reply->readAll());
if (result.isNull())
{
qFatal("Invalid JSON!");
}
reply->deleteLater();
return result.object().toVariantMap();
}
disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
reply->abort();
reply->deleteLater();
return QVariantMap();
}
Is that a good way to proceed? How should I manage the QNetworkReply pointer when other signals are emitted (i.e. error(), sslErrors(), ...)?
QNetworkReply will always emit finished(), even when an error occured.
deleteLater() could even be called in a slot connected to that signal, so that part should be fine.
But I would recommend to look into a more asynchronous approach of handling the request, nested event loops like the on in your getResponse() can lead to "interesting" behavior, because you can basically get into re-entrancy situations in a single threaded program.
void MainWindow::on_pushButton_clicked()
{
QFuture<int> future = QtConcurrent::run(identify); //Thread1
if (future.isFinished())
{
//DoSomething();
}
}
I have this code. I want to run the DoSomething() function after the identify function finished running. Is it possible?
You can pass the QFuture object to a QFutureWatcher and connect its finished() signal to the function or slot DoSomething().
For example:
void MainWindow::on_pushButton_clicked()
{
QFuture<int> future = QtConcurrent::run(identify); //Thread1
QFutureWatcher<int> *watcher = new QFutureWatcher<int>(this);
connect(watcher, SIGNAL(finished()), this, SLOT(doSomething()));
// delete the watcher when finished too
connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
watcher->setFuture(future);
}
void MainWindow::DoSomething() // slot or ordinary function
{
// ...
}
Or you could use a nested event loop to keep the GUI responsive and have everything inside the same function:
void MainWindow::on_pushButton_clicked()
{
QFuture<int> future = QtConcurrent::run(identify); //Thread1
QFutureWatcher<int> watcher;
QEventLoop loop;
// QueuedConnection is necessary in case the signal finished is emitted before the loop starts (if the task is already finished when setFuture is called)
connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()), Qt::QueuedConnection);
watcher.setFuture(future);
loop.exec();
DoSomething();
}
I like this call layout.
auto watcher = new QFutureWatcher<int>(this);
connect(watcher, &QFutureWatcher<int>::finished,
[watcher, this] ()
{
watcher->deleteLater();
auto res = watcher->result();
// ...
});
watcher->setFuture(QtConcurrent::run(identify, param1, param2));
I am trying to get the content of a HTTP request into a QString variable with Qt and C++
QNetworkAccessManager networkManager;
QUrl url("https://someurl.test.com/this-actually-exists");
QNetworkRequest request;
request.setUrl(url);
QNetworkReply* currentReply = networkManager.get(request); // GET
QString reply = QTextCodec::codecForMib(1015)->toUnicode(currentReply->readAll());
Still, the variable reply seems to stay empty. Obviously, I misunderstand the documentation. How do I get this to perform?
You can use two different ways even the synchronous or asynchronous ways to do this. The asynchronous way is :
connect (&networkManager , SIGNAL(finished(QNetworkReply*)) ,this, SLOT(done(QNetworkReply*)));
networkManager.get(request);
And you should read the contents from the returned reply in the slot connected to finished signal in the following way :
void net::done(QNetworkReply * reply)
{
if (reply->error() == QNetworkReply::NoError)
{
data = QString(reply->readAll ());
}
else
{
data = QString(reply->errorString ());
}
}
The synchronous way is like :
QNetworkReply *reply = networkManager.get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
loop.exec();
QByteArray bts = reply->readAll();
QString str(bts);
Here you use an event loop to wait until the reply is finished and then read the available bytes and get the string.
I need to assume you're running an application with an event-loop in place? If not, then it's a bit harder...
If so, replace your last line that builds the reply QString:
connect(currentReply, SIGNAL(finished()), this, SLOT(gotAReply()));
Then you'll have to define another method in your class as a slot that gets triggered as soon as that reply got filled:
void gotAReply()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if (reply)
{
if (reply->error() == QNetworkReply::NoError)
{
QString replyText( reply->readAll() );
}
reply->deleteLater();
}
}
Don't forget: for Signals and Slot to work your class declaration must contain the Q_OBJECT macro.
I was for hours trying to reduce this error to a minimal example, but could not succeed.
I have a GUI with a QPushButton importPCDButton. It should open a QFileDialog and import the selected file. I want to use a thread hoping that the dialog vanishes as soon as I send an update signal to my GUI. So I have an ImportPCD worker class derived from QObject implementing a public slot with the name process(). Inside this, I open the QFileDialog, etc:
void ImportPCD::process() {
std::cout << "foo \n";
QString file_abs = QFileDialog::getOpenFileName(
&*(getControl()->getGuiPtr()),
tr("OpenFile"),
"../data/",
tr("Point Cloud File(*pcd);;ASCII - File(*.asc);;All Files(*)"));
int index = file_abs.lastIndexOf("/");
int size = file_abs.size();
int position = size-index-1;
QString file = file_abs.right(position);
index = file.indexOf(".");
file = file.left(index);
getControl()->setTreeID(file.toStdString());
QString abort;
if(file!=abort)
{
this->path = file_abs.toStdString();
cloudRGB = import ();
emit updateUI();
computeNormals (cloudRGB);
emit updateUI();
principalCurvatures = computeCurvature ();
setRGB ();
getControl()->setCloudPtr(getCloudRGB());
}
emit finished();
}
It also implements the slots updateUI() and finished().
I have a public slot importPCDFile() in my GUI:
void PCLViewer::importPCDFile() {
boost::shared_ptr<QThread> thread_ptr (new QThread);
boost::shared_ptr<ImportPCD> importWorker (new ImportPCD(control));
importWorker->moveToThread(&*thread_ptr);
// connect (ui->importPCDButton , SIGNAL(clicked()), &*thread_ptr, SLOT(start()));
connect (&*thread_ptr, SIGNAL(started()), &*importWorker, SLOT(process()));
// connect (ui->importPCDButton , SIGNAL(clicked()), &*importWorker, SLOT(process()));
connect (&*importWorker, SIGNAL(updateUI()), this, SLOT(updateUI()));
connect (&*importWorker, SIGNAL(finished()), this, SLOT(updateUI()));
connect (&*importWorker, SIGNAL(finished()),&*thread_ptr, SLOT(quit()));
// connect (&*importWorker, SIGNAL(finished()),&*thread_ptr, SLOT(quit()));
connect (&*thread_ptr, SIGNAL(finished()), &*thread_ptr, SLOT(deleteLater()));
// connect (&*thread_ptr, SIGNAL(finished()), &*importWorker, SLOT(deleteLater()));
thread_ptr->start();
}
This method is invoked when I press the button and I get the error:
QThread: Destroyed while thread is still running
What I'm having is a strange problem in typical scenario: QTcpServer's method incomingConnection is overrided in custom class, and any received connection is planned for processing in separate thread on QThreadPool.
Server:
void FooS::incomingConnection(qintptr socketDescriptor)
{
QThreadPool *thread_pool = QThreadPool::globalInstance();
FooSocket *fs = new FooSocket();
fs->setSocket(socketDescriptor);
thread_pool->start(fs);
}
Task:
class FooSocket: public QObject, public QRunnable;
...
private slots:
void connectionIncomingData();
...
void FooSocket::run() {
QTcpSocket *socket = new QTcpSocket();
qDebug() << "SD: " << socketDescriptor; // is correct
if (!socket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Can't set socket descriptor";
emit error(socket->error());
return;
}
// -- had no effect here
// socket->moveToThread(QThread::currentThread());
connect(socket, SIGNAL(readyRead()), this, SLOT(connectionIncomingData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(connectionClosed()));
}
readyRead signal doesn't gets triggered, but socket client is confirmed (tcpdump) to send data..
After making QRunnable to spawn a QThread object with socket logics inside, and toying with setAutoDelete, moveToThread - still no effect.
In order to process events in a QRunnable, a thread needs to have its own event loop, it mustn't rely on the one from the main thread. From what you've shown in your code, your thread quickly starts, then exits without running a loop.
Try adding
QEventLoop loop;
// connect a signal to the event loop's quit() slot
loop.exec();