Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [Solved] How can I evade the error "Cannot send events to objects owned by a different thread"?
QtWS25 Last Chance

[Solved] How can I evade the error "Cannot send events to objects owned by a different thread"?

Scheduled Pinned Locked Moved General and Desktop
11 Posts 3 Posters 32.0k 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.
  • F Offline
    F Offline
    FranzB
    wrote on last edited by
    #1

    Hallo QT community,

    In my application, there is a QProgressBar that ought to update when a thread progresses. Unfortunately, it does not work. When I connect the signal with Qt::AutoConnection (default), there are no errors, but the progress bar updates after the thread terminates. When I use Qt::DirectConnection, the following error appears immediately after the first emit of the signal:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. ..."

    The funny thing is that I can use qDebug() and see the proper output on the console while the thread is running, but anything other than that (e.g. updating a QLabel) does not work or only works after thread termination.

    The thread itself is embedded into third-party code that should stay as-is. It offers a callback function that offers the current progress (an integer). The callback can be used to e.g. call a method of a QObject that emits a signal or writes information to the console via qDebug(). Now I would like to use that information not only on the console, but to update the progress bar, so the signal should be dealt with at once. Is there anything I might have missed?

    I have tried using DirectConnection (brings the above error), AutoConnection (delayed output) and invokeMethod on the slot updating the progress bar (also brings above error). I will gladly test any other ideas. Please help!

    1 Reply Last reply
    0
    • G Offline
      G Offline
      giesbert
      wrote on last edited by
      #2

      Hi FranzB,

      it is clear that DirectConnect will crash as it is forbidden to do UI stuff / use UI components in a non main thread. I'm not sure why your UI does not update, to see that, I would need to look at your code. Can you provide a small simple example?

      Nokia Certified Qt Specialist.
      Programming Is Like Sex: One mistake and you have to support it for the rest of your life. (Michael Sinz)

      1 Reply Last reply
      0
      • F Offline
        F Offline
        FranzB
        wrote on last edited by
        #3

        Hi, sorry for not coming back earlier. I thought I would have some spare time to write a small example on the weekend, but I was too busy.

        It is quite hard to provide a simple example as the thread is embedded into third party code. So I can only offer some bits of code that I hope will clarify the problem.

        In the QObject setting the GUI with the progress bar and the button to start the threaded operation:
        @
        void SomeQObject::slotEvoker(QString filename)
        {
        ThreadTest::callThread(filename);
        }

        void SomeQObject::updateProgress(int percent)
        {
        this->progressBar->setValue(percent);
        //qDebug() << "Progress: " << QString::number(percent);
        }
        @

        In the thread-calling file inside a "ThreadTest" namespace:
        @
        int ThreadTest::showProgress(int percent, void *pContext)
        {
        someOtherQObject->updateProgress(percent);
        //qDebug() << "Progress: " << QString::number(percent);
        return 0;
        }

        DWORD WINAPI ThreadTest::runThread(LPVOID pParam)
        {
        // lengthy operations
        }

        void ThreadTest::callThread(QString filename)
        {
        ThreadContext tc;
        strcpy(tc.fname, filename.toAscii().data());
        strcpy(tc.resultfname, filename.toAscii().data());

        ThirdPartyLib_setProgressCallback(showProgress);

        HANDLE threadHandle;
        threadHandle = CreateThread(NULL, 0, runThread, (LPVOID) &tc, 0, NULL);
        if (threadHandle == NULL)
        exit(-1);
        WaitForSingleObject(threadHandle, INFINITE);

        DWORD exitCode;
        GetExitCodeThread(threadHandle,&exitCode);
        CloseHandle(threadHandle);
        if (exitCode==0)
        qDebug() << "Thread terminated successfully";
        else
        qDebug() << "Thread failed";
        }
        @

        When the user clicks on a button, "slotEvoker" is called via signal. It defers to "callThread". "callThread" uses a struct "ThreadContext" (irrelevant here, I guess) and sets the callback to "showProgress" via a third-party function. Then it proceeds to create a thread and calls "runThread". This function executes the thread and does some lengthy operations. Whenever some progress is made, "showProgress" is called and there we have the mess.

        Short (tldr): slotEvoker->callThread->((runThread->showProgress->updateProgress)) ... (()) is the concurrent part

        The qDebug output of "showProgress" works perfectly, it appears instantly when "showProgress" is called. Of course I can also defer output to another object like this: "someOtherQObject->updateProgress(percent)". Now if there is only a qDebug, that works. If "updateProgress" tries to update the GUI (progress bar), it works only after the thread has terminated. There is no output prior to thread termination - which defies the use of a progress bar.

        Now I would like to know how I can use the output of the callback function ("showProgress", which works perfectly with qDebug, as I said) to update the progress bar while the thread is running.

        Can anyone point me to a solution? I wouldn't have thought that this would appear to me as some kind of rocket science, but I just cannot figure out how to solve that problem.

        1 Reply Last reply
        0
        • G Offline
          G Offline
          giesbert
          wrote on last edited by
          #4

          The point uis that the method showProgress is called in the context of the worker thread. Inside worker threads, you are not allowed to call UI stuff like

          @
          this->progressBar->setValue(percent);
          @

          what you can do is using the meta objects to invoke the method asynchronously:

          @
          int ThreadTest::showProgress(int percent, void *pContext)
          {
          // instead of: someOtherQObject->updateProgress(percent);
          QMetaObject::invokeMethod(someOtherQObject, // obj
          SLOT(updateProgress(int)), // member
          Qt::QueuedConnection, // connection type
          Q_ARG(int, percent)); // val1

          //qDebug() << "Progress: " << QString::number(percent);
          return 0;
          

          }
          @

          If updateProgress is a slot in someOtherQObject, this code should work

          Thjis code was not tested, just written in the forum :-)

          Nokia Certified Qt Specialist.
          Programming Is Like Sex: One mistake and you have to support it for the rest of your life. (Michael Sinz)

          1 Reply Last reply
          1
          • F Offline
            F Offline
            FranzB
            wrote on last edited by
            #5

            Hi,
            Thanks for your answer. Unfortunately, it still does not work. When I use invokeMethod calling the slot as suggested, I receive:

            QMetaObject::invokeMethod: No such method SomeOtherQObject::1updateProgress(int)(int)

            ... although that method/slot clearly exists. Writing @SLOT(updateProgress)@ instead of @SLOT(updateProgress(int))@ leads to a similar result:

            QMetaObject::invokeMethod: No such method SomeOtherQObject::1updateProgress(int)

            Writing "slotProgress" throws no error, but shows the same unwanted behaviour: lazy output only after thread termination.

            You are absolutely right about not calling UI stuff from inside worker threads. I just tried everything that came to my mind after the ways proposed in the doc didn't work. So far I have not been able to grasp the inherent problem of the communication between a worker thread and the GUI main event loop - somehow it just doesn't work and I cannot figure out what is wrong. Do you have any further ideas? They would be greatly appreciated.

            Regards,
            Franz

            1 Reply Last reply
            0
            • G Offline
              G Offline
              giesbert
              wrote on last edited by
              #6

              So, in general, it works, I know that. I have many multi threadded applications and they communicate via signal/slot or invokeMethode.

              If it does not work, it is perhaps a different problem, so If you can reproduce the problem in a small test app, we could have a look at it. But without more source, I have some problems in reproducing it...

              Nokia Certified Qt Specialist.
              Programming Is Like Sex: One mistake and you have to support it for the rest of your life. (Michael Sinz)

              1 Reply Last reply
              0
              • F Offline
                F Offline
                FranzB
                wrote on last edited by
                #7

                Ok, back again from a couple of hours rewriting that damn code. First I tried to reproduce the problem in a small test app, but failed to do so, as it worked pretty much right away.

                I was a little bewildered, but then decided to wrap all the third-party code into my own QThread, so the QThread would invoke the other thread and just parse its outcome to the GUI. Now it works, although it appears like a somewhat complicated solution. As it is neither a beauty contest nor performance-draining, I will happily take this solution.

                Thanks for your help!

                1 Reply Last reply
                0
                • G Offline
                  G Offline
                  giesbert
                  wrote on last edited by
                  #8

                  welcome. Please mark the thread as solved, if it is. To do so, click on edit on your first post and edit the title by adding a [Solved] in front. Thanks.

                  Nokia Certified Qt Specialist.
                  Programming Is Like Sex: One mistake and you have to support it for the rest of your life. (Michael Sinz)

                  1 Reply Last reply
                  0
                  • F Offline
                    F Offline
                    FranzB
                    wrote on last edited by
                    #9

                    Alright, will do that! Is there a way to give you karma / points / kudos / whatever-it-is-called-here to reward you at least somehow for your kind help?

                    Much appreciated, thanks again!

                    1 Reply Last reply
                    0
                    • G Offline
                      G Offline
                      giesbert
                      wrote on last edited by
                      #10

                      Up to now there is no possibility, but it will come the the Q&A stuff ... :-) I glad I could help....

                      Nokia Certified Qt Specialist.
                      Programming Is Like Sex: One mistake and you have to support it for the rest of your life. (Michael Sinz)

                      1 Reply Last reply
                      0
                      • J Offline
                        J Offline
                        JulienMaille
                        wrote on last edited by JulienMaille
                        #11

                        Came accross the same issue today:

                        @FranzB Hi, When I use invokeMethod calling the slot as suggested, I receive:

                        QMetaObject::invokeMethod: No such method SomeOtherQObject::1updateProgress(int)(int)

                        Solved it like that (look at the way the slot is passed to invokeMethod):

                        int ThreadTest::showProgress(int percent, void *pContext)
                           {
                               QMetaObject::invokeMethod(someOtherQObject,         // obj
                                                         "updateProgress",         // member: don't put parameters
                                                         Qt::QueuedConnection,     // connection type
                                                         Q_ARG(int, percent));     // val1
                               return 0;
                           }
                        
                        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