Qthread exec(), event loop and connection with context - qt

I am trying to connect a QThread::finished on another thread with an event loop in order to request a quit()
Consider this code :
#include <QApplication>
#include <QDebug>
#include <QThread>
class DummyThread : public QThread
{
public:
DummyThread::DummyThread(QObject *parent = nullptr) : QThread(parent){}
protected:
virtual void run() override{
QThread::sleep(1);
qDebug()<<"Work done!";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QThread masterThread;
DummyThread childThread;
// This connection is Queued but doesn't work, you can force direct connection but this is not what I want...
QObject::connect(&childThread, &QThread::finished, &masterThread, &QThread::quit /*, Qt::ConnectionType::DirectConnection*/);
childThread.start();
masterThread.start(); //do a exec()
masterThread.wait();
return 0; //No need for an event loop here
}
This is a dummy exemple just to understand the problem here, the connection should be queued and the quit() executed in the masterThread event loop.
But nothing happens here :-(
What am I doing wrong here ?

Related

In a Qt console application, why is a QTimer required for the code to exit properly?

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.

QDialog exec() can not exit process

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog dlg;
dlg.exec();
return a.exec();
}
That's all my code, but when I close the window, The process isn't exit, it seems that drop in the loop a.exec().
Generally speaking, calling any exec is a bad idea, other than QCoreApplication::exec() or QDrag::exec(). The presence of exec() and waitForXxx() methods is an enticing trap for the unwary. Those methods are "easy" to use, but that ease comes at a price of hard to track bugs. Don't use them.
You should simply show the dialog:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMessageBox msg;
msg.setText("Hello");
msg.addButton(QMessageBox::Close);
msg.show();
return a.exec();
}
If you wish to wait for the dialog to be accepted or rejected, you should use the dialog's clickedButton slot. QMessageBox has a long-standing bug that makes the accepted and rejected signals useless :(
// https://github.com/KubaO/stackoverflown/tree/master/questions/messagebox-show-25545652
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <functional>
[...]
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMessageBox msg;
msg.setText("Continue?");
msg.addButton(QMessageBox::Yes);
msg.addButton(QMessageBox::No);
auto onClick = [&msg]() {
auto role = msg.buttonRole(msg.clickedButton());
if (role == QMessageBox::NoRole)
QApplication::quit();
if (role == QMessageBox::YesRole) {
auto label = new QLabel("I'm running");
label->setAttribute(Qt::WA_DeleteOnClose);
label->show();
}
};
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QObject::connect(&msg, &QMessageBox::buttonClicked, onClick);
#else
QObject::connect(&msg, SIGNAL(buttonClicked(QAbstractButton*)),
new FunctorSlot{onClick, &msg}, SLOT(call()));
#endif
msg.show();
return app.exec();
}
#include "main.moc"
For Qt 4, you need the following helper:
// Qt 4 only
struct FunctorSlot : public QObject {
Q_OBJECT
public:
std::function<void()> callable;
template <typename Fun>
FunctorSlot(Fun && fun, QObject * parent = {}) :
QObject{parent}, callable{std::forward<Fun>(fun)} {}
Q_SLOT void call() {
callable();
}
};
Possible solution:
QApplication a(argc, argv);
QDialog dlg;
QTimer::singleShot( &dlg, 0, SLOT(exec()) );
return a.exec();
It will work well. First - application event loop will be started. Then dialog event loop will be executed. After closing of dialog, both dialog and application loop will be finished. Application loop will be terminated automatically (by default), when last window is closed.
But, as noted by #thuga - there are no reason to call exec(). It is enough to call show() method.

why application crashed ,if there are too many events in the eventlist?

The following codes will crash ,and even hang the linux, any ideas about it ?
#include <QCoreApplication>
#include <QString>
#include <QMap>
#include <QList>
#include <QDebug>
#include <QThread>
#include <QTest>
long long emited=0;
long long handled=0;
int flag=0;
class A:public QThread
{
Q_OBJECT
public:
A(QString n):n_(n){moveToThread(this);}
void run() {
QMetaObject::invokeMethod(this, "g",Qt::QueuedConnection);
exec();
}
QString n_;
signals:
void as();
public slots:
void g(){
while(1) {
++emited;
emit as();
}
}
};
class Main:public QObject
{
Q_OBJECT
public slots:
void s0(){}
void s1(){
++flag;
++handled;
A *obj = qobject_cast<A*>(sender());
int nothandle=emited-handled;
--flag;
if(obj) {
qDebug()<<"s1"<<obj->n_<<"not handled:"<<nothandle<<flag;
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread th1,th2;
A a1("a1"),a2("a2");
Main m;
QObject::connect(&a1,SIGNAL(as()),&m,SLOT(s1()),Qt::QueuedConnection);
QObject::connect(&a2,SIGNAL(as()),&m,SLOT(s1()),Qt::QueuedConnection);
a1.start();
a2.start();
return a.exec();
}
It crashes because of this:
while(1) {
++emited;
emit as();
}
The Qt signal queue keeps growing but you are not letting Qt process the signals, so it will keep on going until it crashes. Use a QTimer to avoid freezing your application and to let Qt process your signals.

Does QCoreApplication::quit() cancel all pending events?

This wasn't immediately clear to me from the docs for QCoreApplication::quit().
Are any pending events in the event loop cancelled when the quit() slot is invoked?
Calling QCoreApplication::quit() is the same as calling QCoreApplication::exit(0). There it says
After this function has been called, the application leaves the main event loop and returns from the call to exec().
Since the event loop is left, I would think any pending events are cancelled.
Edit: I made a small test case to show that pending events are indeed cancelled:
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
class MyObject : public QObject
{
Q_OBJECT
public Q_SLOTS:
void start()
{
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
QCoreApplication::quit();
}
protected:
void customEvent(QEvent* event)
{
qDebug() << "Event!";
}
};
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
MyObject o;
QTimer::singleShot(0, &o, SLOT(start()));
return app.exec();
}
#include "main.moc"
In this case, the event posted in MyObject::start() will never arrive. It will, of course, if you remove the call to QCoreApplication::quit().

Qt QNetworkAccessManager does not emit signals

The function CheckSite() is called with an url like http://example.com, it initializes a QNetworkAccessManager object and connect() slots and signals.
The manger->get() call seems work (it generates http traffic) but does not call the slot replyFinished() at the request end.
What's wrong with this code?
#include <QtCore>
#include <QtNetwork>
class ClientHandler : public QObject
{
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
void CheckSite(QString url);
};
void ClientHandler::replyFinished(QNetworkReply *reply) { qDebug() << "DONE"; }
void ClientHandler::CheckSite(QString url) {
QUrl qrl(url);
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
}
Nothing. I wrapped it so it was fully functional and it works fine:
// placed in client.cpp
#include <QtDebug>
#include <QCoreApplication>
/* YOUR CODE */
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
ClientHandler handler;
handler.CheckSite("www.google.com");
return app.exec();
}
#include "client.moc"
It output "DONE" as expected. Maybe the site you're checking really isn't returning? Maybe it needs authentication or is producing ssl errors?
What code do you have around that? Do you spin an event loop somewhere? e.g. qapp.exec() ?

Resources