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



  • 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!



  • 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?



  • 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.



  • 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 :-)



  • 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



  • 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...



  • 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!



  • 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.



  • 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!



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



  • Came accross the same issue today:
    [quote author="FranzB" date="1315313058"]Hi, When I use invokeMethod calling the slot as suggested, I receive:
    QMetaObject::invokeMethod: No such method SomeOtherQObject::1updateProgress(int)(int)[/quote]
    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;
    }@


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.