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. QML multithread client with c++ integration
Forum Updated to NodeBB v4.3 + New Features

QML multithread client with c++ integration

Scheduled Pinned Locked Moved Solved QML and Qt Quick
18 Posts 4 Posters 3.8k Views 2 Watching
  • 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.
  • P PouryaTorabi

    Thanks for your reply, but my problem is that in my loops and long operations, I should use the sockets slots like socket->write and so on, so if I don't make an instance of TCP in my class I don't have access to it. For example I want to upload some heavy files to the server so I should write the file's bytes via socket->write which will freeze the UI, for this reason I want to declare the TCP in a different thread.

    J.HilkJ Offline
    J.HilkJ Offline
    J.Hilk
    Moderators
    wrote on last edited by
    #5

    Hi @PouryaTorabi and welcome,

    @LeLev is right, IIRC you can not set an object that lives in a different thread as contextProperty.

    The idea here is to make a wraper class that lives in the main thread and forwards signals and function calls to your (threaded) tcp class.


    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


    Q: What's that?
    A: It's blue light.
    Q: What does it do?
    A: It turns blue.

    1 Reply Last reply
    1
    • P Offline
      P Offline
      PouryaTorabi
      wrote on last edited by PouryaTorabi
      #6

      I tried @LeLev method, So first I made another class named backend, and registered it to my qml with:

      backend *backendHandler = new backend();
      engine.rootContext()->setContextProperty("backendHandler", backendHandler);
      

      Then in one of backend's functions I create an instance of the TCPhandler class and moved it in a new thread:

      QThread *TcpThread = new QThread();
      tcpHandler *tcp = new tcpHandler();
      tcp->moveToThread(TcpThread);
      TcpThread->start();
      

      In my tcpHandler class I create an instance of the Qtcpsocket. but again the UI froze. The part which cause it is:

      socket->write(mydata);
      

      Since mydata is some large datas. So I tried to write the threads to see if the socket's thread has change or not, and saw that the thread of the created socket is the same as the main thread but the tcpHandler is different. I tried to force the socket to move to tcpHandler's thread by using:

      socket->moveToThread(this->thread());
      

      but it gave the error: "Cannot create children for a parent that is in a different thread".
      So my problem is that the part which froze my UI is socket->write(mydata) which I can't move to another thread and must be in the main thread. The qml engine must also be in the main thread, so what option do I have?
      Actually what I want to do is to upload a large file to the server. (might be around 500mb)

      ODБOïO J.HilkJ kshegunovK 4 Replies Last reply
      0
      • P PouryaTorabi

        I tried @LeLev method, So first I made another class named backend, and registered it to my qml with:

        backend *backendHandler = new backend();
        engine.rootContext()->setContextProperty("backendHandler", backendHandler);
        

        Then in one of backend's functions I create an instance of the TCPhandler class and moved it in a new thread:

        QThread *TcpThread = new QThread();
        tcpHandler *tcp = new tcpHandler();
        tcp->moveToThread(TcpThread);
        TcpThread->start();
        

        In my tcpHandler class I create an instance of the Qtcpsocket. but again the UI froze. The part which cause it is:

        socket->write(mydata);
        

        Since mydata is some large datas. So I tried to write the threads to see if the socket's thread has change or not, and saw that the thread of the created socket is the same as the main thread but the tcpHandler is different. I tried to force the socket to move to tcpHandler's thread by using:

        socket->moveToThread(this->thread());
        

        but it gave the error: "Cannot create children for a parent that is in a different thread".
        So my problem is that the part which froze my UI is socket->write(mydata) which I can't move to another thread and must be in the main thread. The qml engine must also be in the main thread, so what option do I have?
        Actually what I want to do is to upload a large file to the server. (might be around 500mb)

        ODБOïO Offline
        ODБOïO Offline
        ODБOï
        wrote on last edited by
        #7

        @PouryaTorabi said in QML multithread client with c++ integration:

        in one of backend's functions I create an instance of the TCPhandler class and moved it in a new thread

        TcpThread and tcp must be members of your backend class.

        @PouryaTorabi said in QML multithread client with c++ integration:

        In my tcpHandler class I create an instance of the Qtcpsocket. but again the UI froze. The part which cause it is:
        socket->write(mydata);

        how do you call your tcps methodes from the backend ? With signal/slot connections ?

        Can you show how you're doing it please ?

        1 Reply Last reply
        1
        • P PouryaTorabi

          I tried @LeLev method, So first I made another class named backend, and registered it to my qml with:

          backend *backendHandler = new backend();
          engine.rootContext()->setContextProperty("backendHandler", backendHandler);
          

          Then in one of backend's functions I create an instance of the TCPhandler class and moved it in a new thread:

          QThread *TcpThread = new QThread();
          tcpHandler *tcp = new tcpHandler();
          tcp->moveToThread(TcpThread);
          TcpThread->start();
          

          In my tcpHandler class I create an instance of the Qtcpsocket. but again the UI froze. The part which cause it is:

          socket->write(mydata);
          

          Since mydata is some large datas. So I tried to write the threads to see if the socket's thread has change or not, and saw that the thread of the created socket is the same as the main thread but the tcpHandler is different. I tried to force the socket to move to tcpHandler's thread by using:

          socket->moveToThread(this->thread());
          

          but it gave the error: "Cannot create children for a parent that is in a different thread".
          So my problem is that the part which froze my UI is socket->write(mydata) which I can't move to another thread and must be in the main thread. The qml engine must also be in the main thread, so what option do I have?
          Actually what I want to do is to upload a large file to the server. (might be around 500mb)

          J.HilkJ Offline
          J.HilkJ Offline
          J.Hilk
          Moderators
          wrote on last edited by
          #8

          @PouryaTorabi
          To avoid problems and conflicts, refrain from stack variables and constructer initialization in the class that is supposed to be moved to another thread.

          instead create a function to initialize stuff, for example void initClass() then inside your wrapper class connect the started signal of your thread to that initClass function.

          QThread *TcpThread = new QThread();
          tcpHandler *tcp = new tcpHandler();
          tcp->moveToThread(TcpThread);
          connect(TcpThread, &QThread::started, tcp, &tcpHandler::initClass);
          TcpThread->start();
          

          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          1 Reply Last reply
          1
          • P PouryaTorabi

            I tried @LeLev method, So first I made another class named backend, and registered it to my qml with:

            backend *backendHandler = new backend();
            engine.rootContext()->setContextProperty("backendHandler", backendHandler);
            

            Then in one of backend's functions I create an instance of the TCPhandler class and moved it in a new thread:

            QThread *TcpThread = new QThread();
            tcpHandler *tcp = new tcpHandler();
            tcp->moveToThread(TcpThread);
            TcpThread->start();
            

            In my tcpHandler class I create an instance of the Qtcpsocket. but again the UI froze. The part which cause it is:

            socket->write(mydata);
            

            Since mydata is some large datas. So I tried to write the threads to see if the socket's thread has change or not, and saw that the thread of the created socket is the same as the main thread but the tcpHandler is different. I tried to force the socket to move to tcpHandler's thread by using:

            socket->moveToThread(this->thread());
            

            but it gave the error: "Cannot create children for a parent that is in a different thread".
            So my problem is that the part which froze my UI is socket->write(mydata) which I can't move to another thread and must be in the main thread. The qml engine must also be in the main thread, so what option do I have?
            Actually what I want to do is to upload a large file to the server. (might be around 500mb)

            ODБOïO Offline
            ODБOïO Offline
            ODБOï
            wrote on last edited by ODБOï
            #9

            @PouryaTorabi said in QML multithread client with c++ integration:

            froze

            inside your backend don't call tcps methods directly like this :

            //backend.cpp

            tcp->slotName();
            

            use signal/slot instead

            1 Reply Last reply
            1
            • P PouryaTorabi

              I tried @LeLev method, So first I made another class named backend, and registered it to my qml with:

              backend *backendHandler = new backend();
              engine.rootContext()->setContextProperty("backendHandler", backendHandler);
              

              Then in one of backend's functions I create an instance of the TCPhandler class and moved it in a new thread:

              QThread *TcpThread = new QThread();
              tcpHandler *tcp = new tcpHandler();
              tcp->moveToThread(TcpThread);
              TcpThread->start();
              

              In my tcpHandler class I create an instance of the Qtcpsocket. but again the UI froze. The part which cause it is:

              socket->write(mydata);
              

              Since mydata is some large datas. So I tried to write the threads to see if the socket's thread has change or not, and saw that the thread of the created socket is the same as the main thread but the tcpHandler is different. I tried to force the socket to move to tcpHandler's thread by using:

              socket->moveToThread(this->thread());
              

              but it gave the error: "Cannot create children for a parent that is in a different thread".
              So my problem is that the part which froze my UI is socket->write(mydata) which I can't move to another thread and must be in the main thread. The qml engine must also be in the main thread, so what option do I have?
              Actually what I want to do is to upload a large file to the server. (might be around 500mb)

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

              Sockets are special, you can't create them in some thread and then move them to another. Instead, you must create them in the thread that they're going to live in. So, to do that, queue a call in your worker object, which call is going to trigger the socket's creation and initialization. Something along those lines:

              class MyWorker : public QObject
              {
                  Q_OBJECT
              
              public slots:
                   void initSocket();
              
              private:
                  QTcpSocket * socket = nullptr;
              };
              
              void MyWorker::initSocket()
              {
                  socket  = new QTcpSocket(this);
                  // ... More stuff ...
              }
              

              which is used like this (or equivalent):

              MyWorker * worker = new MyWorker;
              
              QThread workerThread;
              workerThread.start();
              
              worker->moveToThread(&workerThread);
              QMetaObject::invokeMethod(worker, &MyWorker::initSocket, Qt::QueuedConnection);
              

              @J.Hilk said in QML multithread client with c++ integration:

              To avoid problems and conflicts, refrain from stack variables

              Just no! Stack is harder, better, faster, stronger. ;)

              Read and abide by the Qt Code of Conduct

              J.HilkJ 1 Reply Last reply
              3
              • kshegunovK kshegunov

                Sockets are special, you can't create them in some thread and then move them to another. Instead, you must create them in the thread that they're going to live in. So, to do that, queue a call in your worker object, which call is going to trigger the socket's creation and initialization. Something along those lines:

                class MyWorker : public QObject
                {
                    Q_OBJECT
                
                public slots:
                     void initSocket();
                
                private:
                    QTcpSocket * socket = nullptr;
                };
                
                void MyWorker::initSocket()
                {
                    socket  = new QTcpSocket(this);
                    // ... More stuff ...
                }
                

                which is used like this (or equivalent):

                MyWorker * worker = new MyWorker;
                
                QThread workerThread;
                workerThread.start();
                
                worker->moveToThread(&workerThread);
                QMetaObject::invokeMethod(worker, &MyWorker::initSocket, Qt::QueuedConnection);
                

                @J.Hilk said in QML multithread client with c++ integration:

                To avoid problems and conflicts, refrain from stack variables

                Just no! Stack is harder, better, faster, stronger. ;)

                J.HilkJ Offline
                J.HilkJ Offline
                J.Hilk
                Moderators
                wrote on last edited by
                #11

                @kshegunov said in QML multithread client with c++ integration:

                @J.Hilk said in QML multithread client with c++ integration:

                To avoid problems and conflicts, refrain from stack variables

                Just no! Stack is harder, better, faster, stronger. ;)

                In 99.99% of all cases I would say yes, it's true. ;-)

                But especially with QThreads I have 3 main reasons why I said that

                • (Allowed) stack size for non main threads can be (default) very limited. IIRC for MacOS is 100k
                • Many Qt classes do internal heap allocation anyway, making a stack declaration kind of moot.
                • Have you tried to do anything with a QTimer that is on the stack of a thread moved class ? Good luck with that

                Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                Q: What's that?
                A: It's blue light.
                Q: What does it do?
                A: It turns blue.

                kshegunovK 1 Reply Last reply
                1
                • P Offline
                  P Offline
                  PouryaTorabi
                  wrote on last edited by PouryaTorabi
                  #12

                  Thanks to you all, now it's working.
                  First of all as you guys mentioned I can't move the socket to a new thread and should create the socket in the thread I want so I created an initSocket() function.
                  To use the socket, as @LeLev said from another thread I should use the signal/slot, and it works correctly now.

                  1 Reply Last reply
                  1
                  • J.HilkJ J.Hilk

                    @kshegunov said in QML multithread client with c++ integration:

                    @J.Hilk said in QML multithread client with c++ integration:

                    To avoid problems and conflicts, refrain from stack variables

                    Just no! Stack is harder, better, faster, stronger. ;)

                    In 99.99% of all cases I would say yes, it's true. ;-)

                    But especially with QThreads I have 3 main reasons why I said that

                    • (Allowed) stack size for non main threads can be (default) very limited. IIRC for MacOS is 100k
                    • Many Qt classes do internal heap allocation anyway, making a stack declaration kind of moot.
                    • Have you tried to do anything with a QTimer that is on the stack of a thread moved class ? Good luck with that
                    kshegunovK Offline
                    kshegunovK Offline
                    kshegunov
                    Moderators
                    wrote on last edited by kshegunov
                    #13

                    @J.Hilk said in QML multithread client with c++ integration:

                    But especially with QThreads I have 3 main reasons why I said that

                    • (Allowed) stack size for non main threads can be (default) very limited. IIRC for MacOS is 100k

                    Half a megabyte, which is more than enough. Even 100k is large enough. Realistically no library will create a thread stack by default with less than 256k from what I've observed, but even that number is quite enough for normal work.

                    • Many Qt classes do internal heap allocation anyway, making a stack declaration kind of moot.

                    Actually that's one very good reason to use it. You don't want to allocate a pointer in the heap, that's such a waste (also time-wise, the stack's much, much faster). Firstly your heap is getting fragmented, and secondly would you ordinarily do:

                    int * a = new int;
                    *a = 20;
                    

                    I wouldn't think so.

                    • Have you tried to do anything with a QTimer that is on the stack of a thread moved class?

                    Actually, I have. There's no problem with it, just give it the proper parent.

                    PS. An important note
                    While I and many others use "stack" as kind of a jargon, what formally is meant is "auto-storage". Bear in mind your worker object is allocated in the heap, so a member of it is not really allocated in the stack. This:

                    struct A
                    {
                        QByteArray x;
                    };
                    
                    A * object = new A;
                    

                    Doesn't mean x is in the stack, it's still in the heap, however as you know the size of the QByteArray (and thus the full size of the structure) the heap allocation is done in one step as it'd been done on the stack. This is formally known as "auto-storage", but again, by merits of jargon we call it a "stack" allocation.

                    Read and abide by the Qt Code of Conduct

                    J.HilkJ 1 Reply Last reply
                    1
                    • kshegunovK kshegunov

                      @J.Hilk said in QML multithread client with c++ integration:

                      But especially with QThreads I have 3 main reasons why I said that

                      • (Allowed) stack size for non main threads can be (default) very limited. IIRC for MacOS is 100k

                      Half a megabyte, which is more than enough. Even 100k is large enough. Realistically no library will create a thread stack by default with less than 256k from what I've observed, but even that number is quite enough for normal work.

                      • Many Qt classes do internal heap allocation anyway, making a stack declaration kind of moot.

                      Actually that's one very good reason to use it. You don't want to allocate a pointer in the heap, that's such a waste (also time-wise, the stack's much, much faster). Firstly your heap is getting fragmented, and secondly would you ordinarily do:

                      int * a = new int;
                      *a = 20;
                      

                      I wouldn't think so.

                      • Have you tried to do anything with a QTimer that is on the stack of a thread moved class?

                      Actually, I have. There's no problem with it, just give it the proper parent.

                      PS. An important note
                      While I and many others use "stack" as kind of a jargon, what formally is meant is "auto-storage". Bear in mind your worker object is allocated in the heap, so a member of it is not really allocated in the stack. This:

                      struct A
                      {
                          QByteArray x;
                      };
                      
                      A * object = new A;
                      

                      Doesn't mean x is in the stack, it's still in the heap, however as you know the size of the QByteArray (and thus the full size of the structure) the heap allocation is done in one step as it'd been done on the stack. This is formally known as "auto-storage", but again, by merits of jargon we call it a "stack" allocation.

                      J.HilkJ Offline
                      J.HilkJ Offline
                      J.Hilk
                      Moderators
                      wrote on last edited by
                      #14

                      @kshegunov said in QML multithread client with c++ integration:

                      Actually, I have. There's no problem with it, just give it the proper parent.

                      Can you elaborate on that, when I try that I get a call to deleted constructor error.


                      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                      Q: What's that?
                      A: It's blue light.
                      Q: What does it do?
                      A: It turns blue.

                      kshegunovK 1 Reply Last reply
                      0
                      • J.HilkJ J.Hilk

                        @kshegunov said in QML multithread client with c++ integration:

                        Actually, I have. There's no problem with it, just give it the proper parent.

                        Can you elaborate on that, when I try that I get a call to deleted constructor error.

                        kshegunovK Offline
                        kshegunovK Offline
                        kshegunov
                        Moderators
                        wrote on last edited by kshegunov
                        #15
                        class Worker : public QObject
                        {
                            Q_OBJECT
                        public:
                            Worker();
                        
                        private:
                            QTimer timer;
                        };
                        
                        Worker::Worker()
                            : timer(this)
                        {
                        }
                        

                        Read and abide by the Qt Code of Conduct

                        J.HilkJ 1 Reply Last reply
                        1
                        • kshegunovK kshegunov
                          class Worker : public QObject
                          {
                              Q_OBJECT
                          public:
                              Worker();
                          
                          private:
                              QTimer timer;
                          };
                          
                          Worker::Worker()
                              : timer(this)
                          {
                          }
                          
                          J.HilkJ Offline
                          J.HilkJ Offline
                          J.Hilk
                          Moderators
                          wrote on last edited by
                          #16

                          @kshegunov oh initializerlist, not my go do method.

                          Ok that works,
                          but I still get QObject::startTimer: Timers cannot be started from another thread errors when calling

                          void startTimer()
                          {
                              m_timer.start(1000);
                          }
                          

                          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                          Q: What's that?
                          A: It's blue light.
                          Q: What does it do?
                          A: It turns blue.

                          kshegunovK 1 Reply Last reply
                          0
                          • J.HilkJ J.Hilk

                            @kshegunov oh initializerlist, not my go do method.

                            Ok that works,
                            but I still get QObject::startTimer: Timers cannot be started from another thread errors when calling

                            void startTimer()
                            {
                                m_timer.start(1000);
                            }
                            
                            kshegunovK Offline
                            kshegunovK Offline
                            kshegunov
                            Moderators
                            wrote on last edited by
                            #17

                            Well, the message is descriptive. Don't start the timer from the wrong thread. ;)

                            Either start the timer before moving it to the other thread, or queue the call over the event loop of the receiving thread.

                            Read and abide by the Qt Code of Conduct

                            J.HilkJ 1 Reply Last reply
                            2
                            • kshegunovK kshegunov

                              Well, the message is descriptive. Don't start the timer from the wrong thread. ;)

                              Either start the timer before moving it to the other thread, or queue the call over the event loop of the receiving thread.

                              J.HilkJ Offline
                              J.HilkJ Offline
                              J.Hilk
                              Moderators
                              wrote on last edited by
                              #18

                              @kshegunov
                              well Kudos to you!
                              Something more to be learned from this thread ;-)


                              Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                              Q: What's that?
                              A: It's blue light.
                              Q: What does it do?
                              A: It turns blue.

                              1 Reply Last reply
                              1

                              • Login

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