PDA

View Full Version : Calling setPlainText() for an object of a different thread leads to assertion failure



bmesing
20th June 2006, 17:17
Hello,

I have the following code that is located in the run() method of my class which is derived from QThread:


_pEventNotifyDlg->_pDateDisplay->setText("Test1");
_pEventNotifyDlg->_pMessageDisplay->setPlainText("Test2");

While the first call works fine, the second causes a crash with the following error message:


QObject::startTimer: timers cannot be started from another thread
QObject::connect: Cannot queue arguments of type 'QRectF'
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 81d1800.
Receiver '' (of type 'QTextDocument') was created in thread 8061188", file kernel/qcoreapplication.cpp, line 293
Aborted

The _pEventNotifyDlg is an instance of a dialog class which belongs to the main thread. _pDateDisplay is a QLabel, and _pMessageDisplay is a QTextBrowser. I do not see that I am sending any events there, but I guess it is triggered by the setPlainText() method call.

Can anybody explain that behaviour to me? Am I misunderstanding something fundamental about threads?

Thanks Ben

QT Version 4.1.3

wysota
20th June 2006, 17:58
Can anybody explain that behaviour to me? Am I misunderstanding something fundamental about threads?

I think the message you got is clear.


Cannot send events to objects owned by a different thread

You can't operate on objects which live in a different thread. You should use postEvent and handle a custom event which will change contents of the widget.

The assertion is because it would be dangerous to modify internal data of another thread (or specifically of an object owned by it). Most of methods which operate on widgets are not thread-safe.

You can read more in the docs -- section about threads. You can also search this forum, there were issues like this (concerning creating objects with parents living in a different thread).

bmesing
20th June 2006, 20:27
Thanks, I've solved my problem by avoiding to use threads.
However it seems quiet a limitation not being able to access widgets directly. It also seems like one has to know many internals, to distinguish which method call raises an event. Probably its best to avoid calls of any QWidget methods from another Thread.

Best regards

Ben

wysota
20th June 2006, 20:35
However it seems quiet a limitation not being able to access widgets directly.
If all widget access was to be thread-safe, you'd have to protect every call that changes internal structures of the widget with a mutex, which would cause a great performance hit.


It also seems like one has to know many internals, to distinguish which method call raises an event.
No, you don't. As you see Qt cares about it for you. And the general rule is not to do any calls to objects living in another thread without being sure they are thread safe (there is a list of thread safe calls in the docs somewhere).


Probably its best to avoid calls of any QWidget methods from another Thread.
Exactly. Most of the calls can be done using signals and slots which are thread-safe in Qt4 and that's how most actions should be handled.In your case it should roughly look like:


_pEventNotifyDlg->_pMessageDisplay->setPlainText("Test2");
connect(this,
SIGNAL(someSignal(const QString &)),
_pEventNotifyDlg->_pMessageDisplay,
SLOT(setPlainText(const QString &))
);
//...

emit someSignal("some text");

jpn
20th June 2006, 20:37
However it seems quiet a limitation not being able to access widgets directly. It also seems like one has to know many internals, to distinguish which method call raises an event. Probably its best to avoid calls of any QWidget methods from another Thread.
Yes, QWidget and all its subclasses are not reentrant. They can only be used from the main thread.

You could use signals and slots and a queued connection to pass a QString from "another" thread to the main thread, and just set the text in the main thread..

wysota
20th June 2006, 20:40
Yes, QWidget and all its subclasses are not reentrant.
I think they are reentrant (at least most of them), just not thread-safe.