如何在不阻塞UI的情况下在QGraphicsScene中移动1000个项目

How to move around 1000 items in a QGraphicsScene without blocking the UI

本文关键字:QGraphicsScene 移动 1000个 项目 情况下 UI      更新时间:2023-10-16

我的QGraphicsScene中有大约1000个图形项目。我想把这1000件商品全部搬到新的位置。新职位之间没有关联,所有职位都应该同时完成
一种方法是迭代这1000个项目,并为每个项目调用setPos!我认为这会阻塞用户界面。另一种方法是在另一个线程中绘制图像,并在QGraphicsScene中设置此图像作为结果
也许你还有别的想法。我很期待听到这个消息!

Qt绘图可以非常快速,如果你了解它的工作原理,即使你想画1000条鱼,它们都独立移动。

在有大量项目的情况下,最糟糕的处理方法是为每个项目创建一个单独的QGraphicsItem/QGraphicsObject,并尝试单独移动和绘制它们。这里人们没有意识到的一个主要问题是,当调用paint(QPainter*painter…)函数时,他们会将画笔设置在画家身上。通常情况下,这是可以的,但这样做会有开销,因为在内部,图形管道会被停滞。对于1000件商品来说,这真的会让事情慢下来。

相反,如果我们将鱼设计为一组鱼,并只创建一个QGraphicsItem,我们就可以在内部跟踪它们的位置,并只调用一次绘制函数。

class SchoolOfFish : QGraphicsObject // QGraphicsObject for signals / slots
{
    Q_OBJECT
    public:
        void UpdateFish();
    protected:
        void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); // overloaded paint function      
    private:
        QList<QPoint> m_fishPositionList;
};

请注意,鱼的所有位置都保存在QPoint对象的QList中。这里可以进行一些优化。首先,我经常看到有人在油漆功能中更新项目位置,导致性能不佳;只有绘图功能才能在油漆中完成。

更新鱼的位置最初可以在计时器上完成,目标可能是每秒30帧。如果速度太慢,那么我们可以创建一个单独的线程来更新所有鱼的位置,并将列表发送回SchoolOfFish对象;所有的图形渲染都必须在主线程上完成。

这种方法实际上只是将鱼群视为一个粒子系统。在以这种方式设计系统后,如果需要,我希望进行的最后一次优化将转移到OpenGl。然而,请注意,您实际上可以获得标准的Qt绘制调用,以使用OpenGl作为QWidget状态的文档

要使用OpenGL进行渲染,只需调用setViewport(新的QGLWidget)。QGraphicsView拥有视口小部件的所有权。

对于在Qt中绘制大量项目,转移到较低级别的绘图界面可能会更好。例如,OpenGL可以在QWindow上实现(可以锚定在主窗口中),请参见http://qt-project.org/doc/qt-5.0/qtgui/openglwindow.html.OpenGL可能有点麻烦,但你不必担心阻塞,我目前的项目正是这样做的,在大约100毫秒内渲染出300000个彩色正方形。

动态场景中setPos()的显著加速可以通过实现

QGraphicsScene scene;
scene.setItemIndexMethod(QGraphicsScene.NoIndex);

这比动态场景的默认QGraphicsScene.BspTreeIndex快得多,但这是有代价的,因为需要查询QGraphicsScene的事件现在会慢得多(例如悬停事件)。

另一种加快速度的方法是减少对象的总数。如果场景使用了大量的QGraphicsItemGroup,可以尝试用普通的QGraphicsItem替换它们,并覆盖它的paint()方法,而不是使用子对象。

也就是说,即使有了这些优化,我发现一旦超过几百个对象,QGraphicsScene的性能仍然非常糟糕。QtQuick似乎能更好地处理大型对象集合。