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"?
Forum Updated to NodeBB v4.3 + New Features

[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.1k Views 1 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.
  • 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