5

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

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
Losiowaty
  • 7,911
  • 2
  • 32
  • 47

2 Answers2

11

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.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • "Ideally these would not be Qt objects at all, although it might be possible to use certain Qt classes if you're very careful"? There is a huge amount of classes that can be used outside the GUI thread... It would be interesting to have numbers, but I would say only a small portion of Qt must not be used outside the GUI thread. – Luca Carlon Apr 28 '13 at 11:16
  • @LucaCarlon My point was that if you do use Qt classes you'll need to verify that they are safe to use, whereas if you use a non-Qt class you can pretty much guarantee that it won't have any non-obvious interactions with the Qt GUI thread. – Jeremy Friesner Apr 28 '13 at 17:12
  • Some guidelines for Qt and multithreading are here: http://www.informit.com/articles/article.aspx?p=1405551&seqNum=4 – Jeremy Friesner Apr 28 '13 at 17:16
  • Still, I think that saying: "Ideally these would not be Qt objects at all" is much more than misleading. Qt classes are intended to be used from whatever thread you want. Only a (small?) set is not (maybe mostly those inheriting from QWidget, QPixmap and some more). Still all the other classes can be used in whatever thread. You can even draw using the QPainter on QImages and QPaintDevice in general from other threads... Not to say about other modules, like QtSql, QtXml etc... – Luca Carlon Apr 29 '13 at 06:30
  • Fair enough; I've deleted the problematic sentence. – Jeremy Friesner Apr 29 '13 at 18:04
  • @LucaCarlon: Until you try the QPainter to drawText on the QImage... good luck :)... Yes, Jeremy was right for discouraging using Qt objects where they aren't needed. There are better alternative to Qt to do any task other than the GUI itself. – Yakov Galka Oct 20 '15 at 10:44
  • QPainter and QImage are both in the gui module, and you can work on QImage's also outside the main thread beautifully (from the docs): "When using QPainter on a QImage, the painting can be performed in another thread than the current GUI thread". The fact that you had problems using fonts on a QImage on a specific platform doesn't make hundreds of Qt classes unlikely to work outside of the main thread. There is HUGE amount of code in Qt outside that module: Qt Core, Qt Multimedia, Qt Network, Qt Sql, Qt Bluetooth, Qt WebSocket etc... and multithreading is a critical part of most applications. – Luca Carlon Oct 20 '15 at 11:04
2

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.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85