I am running a qml application with C++ plugin. Application is pretty simple:
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///ui/views/mainwindow.qml")));
return app.exec();
But qml plugin has a lot of code. To avoid freezes in qml I place object into a thread by moveToThread()
and call methods by QMetaObject::invokeMethod()
asynchronously by using Qt::QueuedConnection
parameter. The problem is the methods I call by invokeMethod
can throw an exception(s) and then the program will crash because I can't catch them:
try {
QMetaObject::invokeMethod(&qlNetwork, "disconnect", Qt::QueuedConnection);
} catch (const std::runtime_error& e) {
emit error(e.what());
}
Of course this code will not work because the call is non-blocking. The question is: how then can I catch exceptions from an object in different thread (QThread) ?
You'd create a wrapper slot that calls disconnect
from another thread and handles the exception.
void ThisClass::wrapperMethod() {
try {
qlNetwork->disconnect();
} catch (const std::runtime_error& e) {
emit error(e.what());
}
}
And then you invoke the wrapper method asynchronously:
QMetaObject::invokeMethod(this, "wrapperMethod", Qt::QueuedConnection);
Make sure wrapperMethod
is a SLOT
or it is defined as Q_INVOKABLE
and ThisClass
instance is moved to a different thread.
Possible solution using lambdas
QTimer *t = new QTimer();
connect(t, &QTimer::timeout, this, [=]() {
t->deleteLater();
try {
qlNetwork->disconnect();
} catch (const std::runtime_error& e) {
emit this->error(e.what());
}
}, Qt::QueuedConnection);
/* don't forget to move the timer to the thread where
you want the lambda to be executed*/
t->moveToThread(targetThread);
t->setSingleShot(true);
t->start(0);
Solution using lambdas with QtConcurrent (Victor Polevoy)
void ThisClass::performDisconnect() {
QtConcurrent::run([this]() {
try {
this->qlNetwork.disconnect();
} catch (const std::runtime_error& e) {
emit error(e.what());
}
});
}