[Solved] How can I evade the error "Cannot send events to objects owned by a different thread"?
-
wrote on 1 Sept 2011, 13:27 last edited by
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!
-
wrote on 1 Sept 2011, 18:41 last edited by
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?
-
wrote on 5 Sept 2011, 10:54 last edited by
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.
-
wrote on 5 Sept 2011, 15:44 last edited by
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 :-)
-
wrote on 6 Sept 2011, 12:44 last edited by
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 -
wrote on 6 Sept 2011, 18:42 last edited by
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...
-
wrote on 7 Sept 2011, 11:55 last edited by
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!
-
wrote on 7 Sept 2011, 12:43 last edited by
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.
-
wrote on 7 Sept 2011, 13:46 last edited by
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!
-
wrote on 7 Sept 2011, 17:35 last edited by
Up to now there is no possibility, but it will come the the Q&A stuff ... :-) I glad I could help....
-
wrote on 2 Feb 2012, 12:53 last edited by JulienMaille
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; }