Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-43230

QML Javascript slot-functions that are connected to the signal of an object living in a worker QThread, are executed in the context of this object’s thread (as if the connection is a Qt::DirectConnection).

    XMLWordPrintable

Details

    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)
      created worker QThread(0x18fc10)
      TestObj "threaded" created in QThread(0x2bbc300)
      TestObj "main" created in QThread(0x2bbc300)

      qml: QML JavaScript handlerFunc
      TestObj "threaded" printThreadId called in QThread(0x18fc10)
      TestObj "main" printThreadId called in QThread(0x18fc10)

      qml: QML JavaScript handlerFunc
      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)
      created worker QThread(0x34f910)
      TestObj "threaded" created in QThread(0x9d05f8)
      TestObj "main" created in QThread(0x9d05f8)

      QML JavaScript handlerFunc
      TestObj "threaded" printThreadId called in QThread(0x9d05f8)
      TestObj "main" printThreadId called in QThread(0x9d05f8)

      QML JavaScript handlerFunc
      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

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            andreaskasberger Andreas Kasberger
            Votes:
            10 Vote for this issue
            Watchers:
            16 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes