5

我正在用 Qt 编写一个程序,它运行 10 个工作线程来计算空间中物体的轨迹。他们还必须绘制对象的路径。我有一个派生 QGraphicsEllipseItem 的“Body”类,其中有一个 QPainterPath。“模拟”类获取世界中的障碍物列表,以及要模拟的身体并运行直到身体与某物碰撞。模拟在单独的线程中运行(使用 moveToThread 完成,而不是通过子类化 QThread)。当身体发生碰撞时,模拟会发出一个信号,表示它已经完成。当所有线程都完成后,我想绘制路径(我通过调用“Body”中的一个方法来绘制路径,该方法在其绘制方法中启用路径绘制)。

不幸的是,我收到了 ASSERT 错误:

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

它们似乎是随机发生的。我尝试了不同的连接类型,但没有结果。
我正在循环启动线程。
我正在使用 Qt 5.0

4

2 回答 2

11

一般来说,使用 Qt,您不能在 GUI 线程(即执行 QApplication::exec() 的线程,通常是 main() 线程)之外执行任何 GUI 操作。

因此,如果您有多个线程操作 QGraphicsItems(尤其是当前属于 QGraphicsScene 的 QGraphicsItems),那么这很可能是您的断言失败的原因。也就是说,当 Qt GUI 线程进行窗口刷新时,它正在从各种 QGraphicsItem 对象中读取数据作为其计算的一部分,并且它希望 QGraphicsItems 在刷新操作期间保持不变。如果在执行刷新例程时(由另一个线程)更改了 QGraphicsItem,则主线程进行的计算可能会出错/损坏,并且偶尔会导致断言失败(和/或其他不需要的行为)。

如果您确实需要使用多个线程,您可能需要做的是让线程在 Qt GUI 线程无法访问的自己的私有数据结构上进行所有计算。然后当线程计算出它们的结果时,它们应该将结果发送回 Qt GUI 线程(通过排队连接或 QApplication::postEvent())。然后 GUI 线程可以查看结果并使用它们来更新 QGraphicsItems 等;这将是“安全的”,因为此更新不能在窗口更新过程中发生。

如果这听起来工作量太大,那么您可以考虑只在 GUI 线程中做所有事情;以这种方式使一切可靠地工作将会变得更加容易和简单。

于 2013-04-28T03:21:19.890 回答
2

正如 Jeremy 所说,Qt 渲染必须在主线程上完成。

虽然您可以将其全部移至主线程,但您可能选择创建单独的线程以提高效率,尤其是在碰撞检测可能是处理器密集型的情况下。处理此问题的最佳方法是将对象的建模及其物理与渲染分离,就像在模型/视图/控制器模式中一样。

创建不是从任何 QGraphicsItem/Objects 派生的主体实例的表示。然后,它们可以在单独的线程上进行计算,并向在主线程中运行的图形对象发出信号,从而更新每个主体实例的图形表示,从而实现轨迹的实时渲染。

于 2013-04-30T08:13:54.383 回答