Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Multithread architecture
QtWS25 Last Chance

Multithread architecture

Scheduled Pinned Locked Moved Solved QML and Qt Quick
3 Posts 2 Posters 1.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    VincentLiu
    wrote on last edited by
    #1

    Hi.

    I have a problem about multithreading architecture, really need you professional suggestions.

    I am using QT5.7 with QML. Now I have five files and wondering what is the right way to design the architecture:

    • main.cpp

    • main.qml

    • worker.h and worker.cpp - communicate with main.qml

    • imgProcessing.h and imgProcessing.cpp - comunicate with worker.h/cpp .......... hope it could be in another thread

    • imgProvider.h and imgProvider.cpp - communicate with worker.h/cpp, inherited from QQuickImageProvider

    A simple work flow would be:

    • main.qml receive an UI event and call corresponding function in worker

    • worker call the functions in imgProcessing to do some image processing

    • the resulted image return to worker

    • In order to display the resulted image, worker send the image to imgProvider

    • In order to show the image, worker emit an signal to main.qml to change the &id of the image source, so that requestPixmap() method in imgProvider can be invoked to return the image.

    I want to put the imgProcessing.h/cpp functions in another thread so that it will not block the action on UI. More specifically, I hope the image update by imgProvider.h/cpp and image processing steps in imgProcessing.h/cpp can be in different thread. Are there any better architecture for this requirement ? Actually I failed to implement it. Below is the main.cpp:

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        qDebug()<<"From main thread: "<<QThread::currentThreadId();
        QThread t;
    
        ImageProvider *provider = new ImageProvider(0);
    
        Worker *worker = new Worker(provider);
    
        worker->moveToThread(&t);
    
        t.start();
    
        QQmlApplicationEngine engine;
    
        engine.addImageProvider(QLatin1String("colors"), provider);
    
        QQmlContext *ctx = engine.rootContext();
        ctx->setContextProperty("WorkerContext", worker);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        return app.exec();
    
    }
    

    And here is the error message:

    QML debugging is enabled. Only use this in a safe environment.
    From main thread:  0x7fff7bb01000
    Worker::Worker
    QQmlEngine: Illegal attempt to connect to Worker(0x7f8e4af7d820) that is in a different thread than the QML engine            
    QQmlApplicationEngine(0x7fff53ac8b00.
    The program has unexpectedly finished.
    

    Thanks for reading up such a long question, I wonder:

    • Is this architecture correct?

    • If so, how to fix the error to make it work?

    Thanks~~~

    kshegunovK 1 Reply Last reply
    0
    • V VincentLiu

      Hi.

      I have a problem about multithreading architecture, really need you professional suggestions.

      I am using QT5.7 with QML. Now I have five files and wondering what is the right way to design the architecture:

      • main.cpp

      • main.qml

      • worker.h and worker.cpp - communicate with main.qml

      • imgProcessing.h and imgProcessing.cpp - comunicate with worker.h/cpp .......... hope it could be in another thread

      • imgProvider.h and imgProvider.cpp - communicate with worker.h/cpp, inherited from QQuickImageProvider

      A simple work flow would be:

      • main.qml receive an UI event and call corresponding function in worker

      • worker call the functions in imgProcessing to do some image processing

      • the resulted image return to worker

      • In order to display the resulted image, worker send the image to imgProvider

      • In order to show the image, worker emit an signal to main.qml to change the &id of the image source, so that requestPixmap() method in imgProvider can be invoked to return the image.

      I want to put the imgProcessing.h/cpp functions in another thread so that it will not block the action on UI. More specifically, I hope the image update by imgProvider.h/cpp and image processing steps in imgProcessing.h/cpp can be in different thread. Are there any better architecture for this requirement ? Actually I failed to implement it. Below is the main.cpp:

      int main(int argc, char *argv[])
      {
          QGuiApplication app(argc, argv);
      
          qDebug()<<"From main thread: "<<QThread::currentThreadId();
          QThread t;
      
          ImageProvider *provider = new ImageProvider(0);
      
          Worker *worker = new Worker(provider);
      
          worker->moveToThread(&t);
      
          t.start();
      
          QQmlApplicationEngine engine;
      
          engine.addImageProvider(QLatin1String("colors"), provider);
      
          QQmlContext *ctx = engine.rootContext();
          ctx->setContextProperty("WorkerContext", worker);
      
          engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
      
          return app.exec();
      
      }
      

      And here is the error message:

      QML debugging is enabled. Only use this in a safe environment.
      From main thread:  0x7fff7bb01000
      Worker::Worker
      QQmlEngine: Illegal attempt to connect to Worker(0x7f8e4af7d820) that is in a different thread than the QML engine            
      QQmlApplicationEngine(0x7fff53ac8b00.
      The program has unexpectedly finished.
      

      Thanks for reading up such a long question, I wonder:

      • Is this architecture correct?

      • If so, how to fix the error to make it work?

      Thanks~~~

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by kshegunov
      #2

      @VincentLiu said in Multithread architecture:

      As the message says you can't do:

      ctx->setContextProperty("WorkerContext", worker);
      

      when worker is in a different thread, it's not allowed. What you should do instead is have signals and slots from your worker to another object that is the context, which lives in the QML's thread.
      Here's a very simple (incomplete) example of how you should do it:

      class WorkerContext : pubic QObject
      {
          Q_OBJECT
      
      public:
          WorkerContext(QObject * parent = NULL)
              : QObject(parent)
          {
          }
      
          Q_INVOKABLE void requestImage()
          {
              emit imageNeeded();
          }
      
      signals:
          void imageNeeded();
          void imageReady(const QImage & image);
      };
      

      Then connect stuff together:

      int main(int argc, char *argv[])
      {
          QGuiApplication app(argc, argv);
      
          // Create the worker object
          QThread workerThread;
      
          // When the application is quitting, so should the worker thread
          QObject::connect(&app, &QCoreApplication::aboutToQuit, &workerThread, &QThread::quit);
      
          Worker * workerObject = new Worker;
          workerObject->moveToThread(&workerThread);
      
          workerThread.start();
      
          // Connect the worker object with the QML context object
          WorkerContext * workerContext = new WorkerContext;
          QObject::connect(workerContext, &WorkerContext::imageNeeded, workerObject, &Worker::processImage);
          QObject::connect(workerObject, &Worker::imageReady, workerContext, &WorkerContext::imageReady);
      
          QQmlApplicationEngine engine;
      
          ImageProvider * provider = new ImageProvider;
          engine.addImageProvider(QLatin1String("colors"), provider);
      
          QQmlContext * ctx = engine.rootContext();
          ctx->setContextProperty("WorkerContext", workerContext);
      
          engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
      
          int result = app.exec();
      
          // You need to wait for the worker thread to exit gracefully
          workerThread.wait();
      
          // Exit the main thread only when the worker thread has finished
          return result;
      }
      

      You can call the context obejct's methods as usual from QML:

      WorkerContext.requestImage(); //< Need an image
      

      and connect the WorkerContext.imageReady signal to your QML code.

      However you may not need the WorkerContext class here at all, you can raise the signals from the ImageProvider when in need for a new image and then connect those signals to the Worker object's slots.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      1
      • V Offline
        V Offline
        VincentLiu
        wrote on last edited by
        #3

        @kshegunov
        Got it. Thanks for your kindness and patience.

        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved