Details
-
Bug
-
Resolution: Out of scope
-
P2: Important
-
None
-
5.3.0, 5.12.2
-
Linux 32Bit
Description
Example: QObject-derived TestObj testThreaded is being instantiated in the application’s main thread, but then immediately moved to be “serviced” in a worker thread. A second instance, testMain, is not moved to the worker thread.
Both objects are made available to QML by setting them as context properties of the root context. The triggerAction signal of testThreaded will be emitted when the timer elapses after 500 milliseconds and the triggerAction signal of testMain will be emitted when the timer elapses after 1 second.
#ifndef TESTOBJ_H #define TESTOBJ_H #include <QObject> #include <QDebug> #include <QThread> class TestObj: public QObject { Q_OBJECT public: TestObj( const QString& a_name = QString(), QObject* a_parent = 0 ) : QObject( a_parent ), m_name( a_name ) { qDebug() << "TestObj" << m_name << "created in" << QThread::currentThread(); } TestObj( const TestObj& a_other ) : QObject(), m_name( a_other.m_name ) { } signals: void triggerAction(); public slots: void printThreadId() const { qDebug() << "TestObj" << m_name << "printThreadId called in" << QThread::currentThread(); } private: QString m_name; }; #endif // TESTOBJ_H
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QDebug> #include <QThread> #include <QQmlContext> #include <QTimer> #include "testobj.h" int main( int a_argc, char* a_argv[] ) { QGuiApplication app( a_argc, a_argv ); qRegisterMetaType<TestObj>("TestObj"); qDebug() << "main function" << QThread::currentThread(); QThread worker; qDebug() << "created worker" << &worker; TestObj testThreaded( "threaded" ); testThreaded.moveToThread( &worker ); TestObj testMain( "main" ); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty( "testThreaded", &testThreaded ); engine.rootContext()->setContextProperty( "testMain", &testMain ); engine.load( QUrl( QStringLiteral( "qrc:///main.qml" ) ) ); QTimer::singleShot( 500, &testThreaded, SIGNAL( triggerAction() ) ); QTimer::singleShot( 1000, &testMain, SIGNAL( triggerAction() ) ); worker.start(); int result = app.exec(); worker.quit(); return result; }
Then in QML, a Javascript slot-function is connected to the triggerAction signal emitted by both objects.
import QtQuick 2.2 import QtQuick.Window 2.1 Window { visible: true width: 100 height: 100 function handlerFunc() { console.log( "QML JavaScript handlerFunc" ); testThreaded.printThreadId(); testMain.printThreadId(); } Component.onCompleted: { testThreaded.triggerAction.connect( handlerFunc ); testMain.triggerAction.connect( handlerFunc ); } }
Now, using Qt 5.3, whenever the signal triggerAction is emitted by testThreaded, our handlerFunc is called and the thread-ID of the worker thread in which testThreaded lives will be printed for both instances. Whenever the signal triggerAction is emitted by testMain, our handlerFunc is called and the thread-ID of the main thread will be printed for both instances:
main function QThread(0x2bbc300) qml: QML JavaScript handlerFunc qml: QML JavaScript handlerFunc
created worker QThread(0x18fc10)
TestObj "threaded" created in QThread(0x2bbc300)
TestObj "main" created in QThread(0x2bbc300)
TestObj "threaded" printThreadId called in QThread(0x18fc10)
TestObj "main" printThreadId called in QThread(0x18fc10)
TestObj "threaded" printThreadId called in QThread(0x2bbc300)
TestObj "main" printThreadId called in QThread(0x2bbc300)
If the same example is run on Qt 4.8 it will print the thread-ID of the main thread in all cases. This clearly indicates that QML threading/signaling behavior has changed between these versions:
main function QThread(0x9d05f8) QML JavaScript handlerFunc QML JavaScript handlerFunc
created worker QThread(0x34f910)
TestObj "threaded" created in QThread(0x9d05f8)
TestObj "main" created in QThread(0x9d05f8)
TestObj "threaded" printThreadId called in QThread(0x9d05f8)
TestObj "main" printThreadId called in QThread(0x9d05f8)
TestObj "threaded" printThreadId called in QThread(0x9d05f8)
TestObj "main" printThreadId called in QThread(0x9d05f8)
Furthermore, if the signal/slot connection is made using the QML Connections construct instead:
Connections { target: testThreaded onTriggerAction: { handlerFunc(); } } Connections { target: testMain onTriggerAction: { handlerFunc(); } }
then the application aborts with error: QQmlEngine: Illegal attempt to connect to TestObj(0x29f690) that is in a different thread than the QML engine QQmlApplicationEngine(0x29f6dc).
Attached both projects as zip