For the purpose of the resolution, I've created a TestApp that repeat the same problem that I have.
I'm porting my software from Qt 4.8 to Qt 5.1.
My first program was multithreaded, and was working smoothly with QML, provided that the classes were thread safe. But now I get this message :
QObject::connect: No such slot TestApp::run() in ..\ThreadingTest\main.cpp:21
QQmlEngine: Illegal attempt to connect to TestApp(0x29cfb8) that is in a different thread than the QML engine QQmlEngine(0x2f3e0f8).
This is the code that reproduce the error :
main.cpp :
#include <QtGui/QGuiApplication>
#include <QQmlContext>
#include <QThread>
#include "qtquick2applicationviewer.h"
#include "testapp.h"
int main(int argc, char *argv[])
{
int out;
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
TestApp * testapp = new TestApp();
QThread * testappThread;
testappThread = new QThread();
QObject::connect(testappThread, SIGNAL(started()), testapp, SLOT(run()));
testapp->moveToThread(testappThread);
testappThread->start();
viewer.rootContext()->setContextProperty("TestApp", testapp);
viewer.setMainQmlFile(QStringLiteral("qml/ThreadingTest/main.qml"));
viewer.showExpanded();
out = app.exec();
testappThread->quit();
testappThread->wait();
delete testapp;
delete testappThread;
return out;
}
testapp.h :
#ifndef TESTAPP_H
#define TESTAPP_H
#include <QObject>
#include <QString>
#include <QTimer>
#include <QReadWriteLock>
#define HELLOWORLD "Hello World !"
extern QReadWriteLock HelloWorldLock;
class TestApp : public QObject
{
Q_OBJECT
Q_PROPERTY(QString HelloWorld READ getHelloWorld WRITE setHelloWorld NOTIFY HelloWorldChanged)
public:
explicit TestApp(QObject *parent = 0);
virtual ~TestApp();
QString getHelloWorld();
void setHelloWorld(QString);
public slots:
void run();
void toggleHelloWorld();
signals:
void HelloWorldChanged();
private:
QString m_HelloWorld;
QTimer * m_Timer;
};
#endif // TESTAPP_H
testapp.cpp :
#include "testapp.h"
QReadWriteLock HelloWorldLock(QReadWriteLock::Recursive);
TestApp::TestApp(QObject *parent) :
QObject(parent)
{
HelloWorldLock.lockForWrite();
m_HelloWorld = HELLOWORLD;
HelloWorldLock.unlock();
m_Timer = new QTimer(this);
connect(m_Timer, SIGNAL(timeout()), this, SLOT(toggleHelloWorld()));
}
TestApp::~TestApp() {
m_Timer->stop();
delete m_Timer;
}
QString TestApp::getHelloWorld() {
HelloWorldLock.lockForRead();
QString out = m_HelloWorld;
HelloWorldLock.unlock();
return out;
}
void TestApp::setHelloWorld(QString text) {
HelloWorldLock.lockForWrite();
m_HelloWorld = text;
HelloWorldLock.unlock();
emit HelloWorldChanged();
}
void TestApp::run() {
m_Timer->start(1000);
}
void TestApp::toggleHelloWorld() {
HelloWorldLock.lockForWrite();
if(m_HelloWorld == "") {
m_HelloWorld = HELLOWORLD;
}
else {
m_HelloWorld = "";
}
HelloWorldLock.unlock();
emit HelloWorldChanged();
}
main.qml :
import QtQuick 2.0
Rectangle {
width: 360
height: 360
Text {
text: TestApp.HelloWorld
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
My program is quite complex (a lot of properties and classes to share with the interface) and I wouldn't like to have to create an interface class just to connect my properties... Do you have any suggestions to cope with this issue ?
You don't need to thread the app yourself cause in Qt5, the QML 2 engine is already massively multithreaded, so just launch the QQuickView, expose the C++ parts you need to the context, set the QML file in it, and show(). It's sufficient. Don't try to modify the QML thread by yourself, this is really more complex that it was in QML1.
Various things here:
Moving QTimers between threads is problematic, especially if they are started
The secondary thread never calls exec(), so a QTimer living in it won't fire (but I suspect it does fire due to you creating it and then using moveToThread
Your property accessors will certainly be called by the QML engine on its thread (i.e the main thread in this example), so they need to be thread-safe as you stated.
My suggestion would be to restructure things to avoid using moveToThread entirely, and then see what issues remain.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With