I'm trying to execute a network request and wait for the response before moving on. Whereas using a QEventLoop works to wait for the request, the problem is if there are other signals fired in other parts of the program they will execute first.
Here is an example of the code re-producing the problem. You can see the network request (I'm using http://example.com here just for example) is clearly going to be issued before the second singleShot timer will fire.
#include <QApplication>
#include <QTimer>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
void longFunction()
{
qDebug() << "Starting Long Function";
QEventLoop localEventLoop;
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://example.com")));
QObject::connect(reply, &QNetworkReply::finished, &localEventLoop, &QEventLoop::quit);
// I wish to block here
// - do not want to pass this point and run
// other events if fired from the main event loop
localEventLoop.exec();
qDebug() << "Finishing Long Function";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer::singleShot(0, &longFunction);
QTimer::singleShot(1, []() {
qDebug() << " -- Some other function that was issued after";
qDebug() << " -- 'longFunction' started but before 'longFunction' ended";
});
return a.exec();
}
Here is the output:
Here is the desired output:
Edit
To add some more info. The code above is obviously an over simplification of the problem as I can't easily post all of the code. Basically whats happening is there is code somewhere else in my project that will emit a signal that is connected to the longFunction() above. Obviously I want to wait for a response from the REST call before continuing because the response will dictate the elements later in that function. The issue is that at some point later (potentially before the REST call finishes), some other parts of my code might trigger some other signal and I don't want those other signals to be processed. I just want to wait for the ONE connection on the localEventLoop to be processed.
Any help would be greatly appreciated
QTimer::singleShot(0, &longFunction);
is an asynchronous function. It returns before void longFunction() executed the QEventLoop. The result is that your second QTimer::singleShot(1, []() { will trigger immediately. The solution is to move QEventLoop outside your function, before QTimer::singleShot(1, []() {
Related
The code is in C++/Qt.
Basically, I wanted to create a timer (but not started) and started it later when necessary. But it did not work.
If I created and started the timer immediately, it worked as expected.
The idea of the code is like the followings:
mytimer.cpp:
#include "mytimer.h"
#include <QtCore>
MyTimer::MyTimer()
{
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(mySlot()));
// timer->start(1000);
}
void MyTimer::mySlot()
{
qDebug()<<"timer executed";
}
MyTimer::startTimer(void)
{
timer->start(1000);
}
Note: startTimer() is triggered from another thread with an signal. I did debug the code and startTimer() function did get called as expected but the Timer did NOT start.
And in the main.cpp:
#include <QApplication>
#include "mytimer.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyTimer mtimer;
qDebug()<<"DONE";
return a.exec();
}
I don't know why I could not start the timer later.
This is the main problem: "Note: startTimer() is triggered from another thread with an signal". However, you instantiate your MyTimer on the main thread which is different, since you try to start your timer on a different thread. You should either create your timer on the thread you want timeout to trigger or move your timer to that thread, and then connect your timer to timeout and slot. For instance, you could do like this maybe (not checked if it would really work):
MyTimer::startTimer(void)
{
timer->moveToThread(QThread::currentThread());
connect(timer, &QTimer::timeout, this, &MyTimer::mySlot);
timer->start(1000);
}
However, I would suggest the following approach, since moving objects between threads might mess up with your code, make it harder to manage, so the simpliest possible solution should be prefered to solve your problem, in my opinion.
MyTimer::startTimer(void)
{
// Create your timer on the thread you want
timer = new QTimer(this);
// Connect signal and slot
connect(timer, &QTimer::timeout, this, &MyTimer::mySlot);
// Start the timer
timer->start(1000);
}
Which one to use is up to you, but you should know that connecting signals and slots between different threads is not working under Qt, and all of the connections should be instantiated on the same thread, and actions triggering these connections should also be executed on the same thread where connection has been introduced.
I've wrote this code looking at examples online of how I'm supposed to run a console program that doesn't just run and quit and one that does. Based on a Qt console application. This one here, I wanted it to quit. I've understood pretty much everthing excepth the QTimer::singleShot line. If the line is commented out, the application will run but will not quit. If it is left, the application will run and quit as expected. Can anyone explain to me why?
dostuff.h
#ifndef DOSTUFF_H
#define DOSTUFF_H
#include <QObject>
#include <iostream>
class DoStuff: public QObject
{
Q_OBJECT
public :
DoStuff(QObject *parent = 0);
public slots:
void run();
signals:
void finished();
};
#endif // DOSTUFF_H
And the implementation dostuff.cpp
#include "dostuff.h"
DoStuff::DoStuff(QObject *parent):QObject(parent)
{
}
void DoStuff::run(){
for (int i = 0; i < 10000; i++){
std::cout << "Processing " << i << std::endl;
}
emit(finished());
}
My main.cpp
#include <QCoreApplication>
#include <QTimer>
#include "dostuff.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
DoStuff *dostuff = new DoStuff(&a);
QObject::connect(dostuff,SIGNAL(finished()),&a,SLOT(quit()));
dostuff->run();
// WHY THIS??
QTimer::singleShot(10,dostuff,SLOT(run()));
return a.exec();
}
QTimer is not required to exit properly; You just need to provide a way to get your application to break the event loop at some point. In GUI application, Qt does that automatically when the last window is closed.
In Console applications, you can:
Either run your application without an event loop (if you have a straight-forward simple control flow in your application).
Or (if you require an event loop to handle some events or cross thread signal/slots) you need to have some event that makes your application break the event loop and quit. This event should only be triggered when the application has finished its job.
The code sample you have in your question is really simple, and does not require an event loop to run properly. The only effect the QTimer has in your code is that it delays execution for 10 ms. Here is the same code sample without running an event loop:
#include <QtCore>
class DoStuff: public QObject
{
Q_OBJECT
public :
DoStuff(QObject *parent = 0) : QObject(parent) {}
public slots:
void run() {
for (int i = 0; i < 10000; i++){
qInfo() << "Processing " << i;
}
emit finished();
}
signals:
void finished();
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
DoStuff dostuff;
QObject::connect(&dostuff, &DoStuff::finished,
&a, &QCoreApplication::quit);
dostuff.run();
return 0; //no event loop required
}
#include "main.moc"
If you start an event loop, you may notice that the quit slot does not work when not using QTimer::singleShot. The reason for this is that quit is called before the event loop is even started (and the call has no effect at all). That's why according to the docs, it is recommended to connect to quit using a queued connection:
It's good practice to always connect signals to this slot using a QueuedConnection. If a signal connected (non-queued) to this slot is emitted before control enters the main event loop (such as before "int main" calls exec()), the slot has no effect and the application never exits. Using a queued connection ensures that the slot will not be invoked until after control enters the main event loop.
So, if you want to have an event loop in your code above, you just need to connect using a Qt::QueuedConnection:
#include <QtCore>
class DoStuff: public QObject
{
Q_OBJECT
public :
DoStuff(QObject *parent = 0) : QObject(parent) {}
public slots:
void run() {
for (int i = 0; i < 10000; i++){
qInfo() << "Processing " << i;
}
emit finished();
}
signals:
void finished();
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
DoStuff dostuff;
QObject::connect(&dostuff, &DoStuff::finished,
&a, &QCoreApplication::quit,
Qt::QueuedConnection);
// ^^^^^^^^^^^^^^^^
// use a queued connection
dostuff.run();
return a.exec(); //start an event loop
}
#include "main.moc"
Timer is needed to postpone execution. Since you want to have a running event loop, a.exec() has to be called, then the timer executes your code. When your code finishes running, it triggers finished signal, that is tied to QCoreApplication::quit - that's the needed exit for event loop running inside a.exec().
Btw, you have to remove: dostuff->run(); from your code.
Using a QEventLoop to block execution in a Qt program, how can you connect 10 individual signals to the one loop in such a way that it won't unblock until all 10 signals are received?
Please avoid using nested loops when possible. But if you are absolutely sure that you have no way around, you need to have a way to store which of the signals have fired and which of them haven't, and quit the event loop (i.e. maybe by emitting a signal connected to your event loop's QEventLoop::quit) only when all signals have fired.
Here is a minimum example that uses 10 QTimers with different intervals, and waits for all of them to fire before quitting a nested event loop:
#include <QtCore>
#include <algorithm>
int main(int argc, char* argv[]) {
QCoreApplication a(argc, argv);
const int n = 10;
//10 timers to emit timeout signals on different intervals
QTimer timers[n];
//an array that stores whether each timer has fired or not
bool timerFired[n]= {};
QEventLoop loop;
//setup and connect timer signals
for(int i=0; i<n; i++) {
timers[i].setSingleShot(true);
QObject::connect(&timers[i], &QTimer::timeout, [i, &timerFired, &loop]{
qDebug() << "timer " << i << " fired";
timerFired[i]=true;
//if all timers have fired
if(std::all_of(std::begin(timerFired), std::end(timerFired),
[](bool b){ return b; }))
loop.quit(); //quit event loop
});
timers[i].start(i*i*100);
}
qDebug() << "executing loop";
loop.exec();
qDebug() << "loop finished";
QTimer::singleShot(0, &a, &QCoreApplication::quit);
return a.exec();
}
I have a strange behavior with Lambda and timer on Qt 5.7.1. Probably a mistake from me.
I start a connection with a socket and set a timer to check whether it was connected or not after a certain amount of time.
The signal connected of the socket will stop the time.
However, with the following implementation, the timer does not stop even if the connected signal is called.
constructor:
m_connectTimeout.setInterval(5000);
connect(&m_socket, &QLocalSocket::connected, [&]()
{
// this is called first and should stop the timer.
m_connectTimeout.stop();
});
connect(&m_connectTimeout, &QTimer::timeout, [&](){
// this is still called
});
Here is a minimum example with problem reproducible on Qt5.7.1 and Windows 10.
#include <QtCore>
#include <QtNetwork>
#define PIPENAME "testbug"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer timer;
QLocalSocket socketClient, *socketServer;
QLocalServer server;
timer.setInterval(2000);
QObject::connect(&timer, &QTimer::timeout, [&]
{
qDebug() << "client connection timed out";
timer.stop();
});
QObject::connect(&socketClient, &QLocalSocket::connected, [&]
{
qDebug() << "client connected";
timer.stop();
});
QObject::connect(&server, &QLocalServer::newConnection, [&]
{
qDebug() << "server got connection";
socketServer = server.nextPendingConnection();
});
server.setSocketOptions(QLocalServer::WorldAccessOption);
server.listen(PIPENAME);
qDebug() << "client connecting. . .";
socketClient.connectToServer(PIPENAME, QLocalSocket::ReadWrite);
timer.start();
return a.exec();
}
Output of the program:
client connecting. . .
client connected
server got connection
client connection timed out
I also noticed it's not always reproducible and seems somehow random.
Actually it seems the code works, it's just that the connection is so fast, that the timer.stop is called before the timer.start.
Starting the timer before calling connect to server seems to solve the issue
m_timer.start();
m_socketClient.connectToServer(PIPENAME, QLocalSocket::ReadWrite);
This would mean that connectToServer does some calls on the event loop in the background, allowing the slots to be called, even before the next line is executed.
Qt documentation states that it is possible to connect two signals together:
It is even possible to connect a signal directly to another signal.
I tried:
connect(x, SIGNAL(S()), y, SIGNAL(func()));
and it works as mentioned, but Qt documentation continues:
(This will emit the second signal immediately whenever the first is emitted.)
Does this mean that QueuedConnection will not work correctly? Can I connect two signals across threads?
The reason I am asking this is because I solved a class of crashes on an application by avoiding this, but I am not sure if this was related to connecting signals together.
It shouldn't be a great deal different from a signal/slot connection. Let's take a look at underlying mechanism of signals/slots. There is an event queue in each thread which maintains signals (events) that have been emitted but not processed yet. So whenever the execution returns to the event loop the queue is processed. Event loop itself doesn't handle the events. Rather it delivers them to the objects so they can handle it. In this special case, I suppose that the object would emit another signal which would be inserted in the queue. When the execution returns to event loop the new signal is handled by the object again. Here is a test which proves the above argument.
If you run the codes attached, the output would be:
before signal()
after signal()
slot() called
which means defining a signal-signal connection type as queued between threads have the expected queued behaviour, that rejects the argument which it is always immediate. If you define it as direct, the output would be:
before signal()
slot() called
after signal()
as expected. it doesn't generate any errors or warnings, and program doesn't crash as well.Yet this simple example doesn't prove it works for a large and complex one as well.
main.cpp:
#include <QtGui/QApplication>
#include "dialog.h"
#include "testssconnection.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestSignalSignalConnection * t = new TestSignalSignalConnection();
t->start();
return a.exec();
}
testssconnection.h:
#ifndef TESTSSCONNECTION_H
#define TESTSSCONNECTION_H
#include <QObject>
#include <QThread>
class TestSignalSignalConnection : public QThread
{
Q_OBJECT
public:
explicit TestSignalSignalConnection(QObject *parent = 0);
void run();
signals:
void signal1();
void signal2();
public slots:
void slot();
};
#endif // TESTSSCONNECTION_H
testssconnection.cpp:
#include "testssconnection.h"
#include <QtCore>
TestSignalSignalConnection::TestSignalSignalConnection(QObject *parent) :
QThread(parent)
{
}
void TestSignalSignalConnection::run()
{
TestSignalSignalConnection *t = new TestSignalSignalConnection();
this->connect(this,SIGNAL(signal1()),t,SIGNAL(signal2()), Qt::QueuedConnection);
t->connect(t,SIGNAL(signal2()), t,SLOT(slot()), Qt::DirectConnection);
qDebug() << "before signal()";
emit signal1();
qDebug() << "after signal()";
exec();
}
void TestSignalSignalConnection::slot()
{
qDebug() << "slot() called";
}
Take a look at qt-project, its a great wiki page about Threads and signals.
Threads, Events and QObjects::Signals and slots across threads