Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing from multiple threads in Qt

I'm writing a program in Qt, which runs 10 worker threads which calculate the trajectory of an object in space. They also have to draw the path of the object. I have a "Body" class deriving QGraphicsEllipseItem and it has a QPainterPath in it. The "Simulation" class takes a list of obstacles in the world, and the body to simulate and runs until the body collides with something. Simulation runs in a separate thread ( done with moveToThread, not by subclassing QThread). When the body collides, the Simulation emits a signal saying that it finished. When all threads have finished I'd like to draw the paths (I do it by invoking a method in "Body" which enables path drawing in its draw method).

Unfortunately I get ASSERT errors :

ASSERT: "!unindexedItems.contains(item)" in file graphicsview\qgraphicsscenebsptreeindex.cpp, line 364

They happen seemingly randomly. I've tried different connection types, to no result.
I'm starting the threads in a loop.
I'm using Qt 5.0

like image 842
Losiowaty Avatar asked Apr 28 '13 00:04

Losiowaty


2 Answers

Generally speaking, with Qt you can't do any GUI operations outside of the GUI thread (i.e. the thread that is executing QApplication::exec(), which is typically the main() thread).

So if you have multiple threads manipulating QGraphicsItems (especially QGraphicsItems that are currently part of a QGraphicsScene), that is likely the cause of your assertion failures. That is, when the Qt GUI thread is doing its window refresh, it is reading data from the various QGraphicsItem objects as part of its calculations, and it expects the QGraphicsItems to remain constant for the duration of the refresh operation. If a QGraphicsItem is changed (by another thread) while the refresh routine is executing, then the calculations made by the main thread can become wrong/corrupted, and that occasionally causes an assertion failure (and/or other unwanted behaviors).

If you really need to use multiple threads, what you'll probably need to do is have the threads do all their calculations on their own private data structures that the Qt GUI thread has no access to. Then when the threads have computed their results, they should send the results back to the Qt GUI thread (via queued connection or QApplication::postEvent()). The GUI thread can then look at the results and use them to update the QGraphicsItems, etc; this will be "safe" because this update can't happen in the middle of a window update.

If that sounds like too much work, then you might consider just doing everything in the GUI thread; it will be much easier and simpler to make everything work reliably that way.

like image 99
Jeremy Friesner Avatar answered Nov 15 '22 07:11

Jeremy Friesner


As mentioned by Jeremy, Qt rendering must be done on the main thread.

While you could move it all to the main thread, you've likely chosen to create separate ones for efficiency, especially as collision detection can be processor intensive. The best way to handle this is to split the modelling of the objects and their physics from their rendering, as you would in a Model / View / Controller pattern.

Create representations of the body instances that are not derived from any QGraphicsItem/Objects. These can then do their calculations on separate threads and have signals to graphics objects that are running in the main thread, which updates each body instance's graphic representation, allowing real-time rendering of the trajectories.

like image 36
TheDarkKnight Avatar answered Nov 15 '22 06:11

TheDarkKnight