C++ Qt Как оптимизировать добавление и удаление большого кол-ва объектов на сцену?

проблема заключается в добавлении большого кол-ва объектов на сцену, работаю с QGraphicsView, QGraphicsScene, и QGraphicsEllipceItem. Приходят данные с сервера по точке на сцене и я ее добавляю.
Объект точки
Как выяснилось, заводить таймер на каждый объект было не очень хорошей идеей, предположительно именно из-за большого кол-ва таймеров со временем система начинает тормозить, но по таймеру отрабатывает изменение Opacity, для плавного появления и исчезновения в методе changeDotDisplay(), как только opacity опускается обратно до 0, вызывается сигнал на удаления точки со сцены.

Dot::Dot(int id, int color_id, int dot_width, int dot_height, float x_position, float y_position)
{
    _id = id;
    _color_id = color_id;

    setPos(x_position, y_position);
    setRect(x_position, y_position, dot_width, dot_height);
    setOpacity(0);

    if (_id != -1)
        setDotColor();
    else
        setPen(QPen(My_Color::MainColor::White));


    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &Target::changeTargetDisplay);
    timer->start(1000/60);

    setData(1, true);
}

void Dot::changeDotDisplay()
{
    if (data(0).toBool())
    {
        if (!is_timeout)
        {
            if (data(1).toBool())
            {
                if (opacity <= 1.00)
                {
                    opacity += dot_appearence_speed;
                    setOpacity(opacity);
                }
                else
                    is_timeout = true;
            }
        }
        else
        {
            if (data(1).toBool())
            {
                if (opacity >= 0)
                {
                    opacity -= dot_disappearence_speed;
                    setOpacity(opacity);
                }
                else
                {
                    emit destroy_dot();
                    is_timeout = false;
                    setData(0, false);
                    setData(1, false);
                    timer->stop();
                    timer->disconnect();
                    timer->deleteLater();
                    timer = nullptr;                    
                }
            }
        }
    }
}

void Dot::setDotColor()
{
    if (_color_id % 10 == 0)
    {
        setPen(QPen(My_Color::RedTones::Red));
        setBrush(QBrush(My_Color::RedTones::Red));
    }
    else if (_color_id % 10 == 1)
    {
        setPen(QPen(My_Color::PinkTones::DeepPink));
        setBrush(QBrush(My_Color::PinkTones::DeepPink));
    }
    else if (_color_id % 10 == 2)
    {
        setPen(QPen(My_Color::OrangeTones::OrangeRed));
        setBrush(QBrush(My_Color::OrangeTones::OrangeRed));
    }
    else if (_color_id % 10 == 3)
    {
        setPen(QPen(My_Color::YellowTones::Yellow));
        setBrush(QBrush(My_Color::YellowTones::Yellow));
    }
    else if (_color_id % 10 == 4)
    {
        setPen(QPen(My_Color::PurpleTones::Violet));
        setBrush(QBrush(My_Color::PurpleTones::Violet));
    }
    else if (_color_id % 10 == 5)
    {
        setPen(QPen(My_Color::GreenTones::Lime));
        setBrush(QBrush(My_Color::GreenTones::Lime));
    }
    else if (_color_id % 10 == 6)
    {
        setPen(QPen(My_Color::GreenTones::Green));
        setBrush(QBrush(My_Color::GreenTones::Green));
    }
    else if (_color_id % 10 == 7)
    {
        setPen(QPen(My_Color::BlueTones::Aqua));
        setBrush(QBrush(My_Color::BlueTones::Aqua));
    }
    else if (_color_id % 10 == 8)
    {
        setPen(QPen(My_Color::BlueTones::Blue));
        setBrush(QBrush(My_Color::BlueTones::Blue));
    }
    else if (_color_id % 10 == 9)
    {
        setPen(QPen(My_Color::BlueTones::DodgerBlue));
        setBrush(QBrush(My_Color::BlueTones::DodgerBlue));
    }
}


Добавление точки на сцену
Приходят параметры точки с сервера, расчитываются координаты её положения на сцене, устанавливаются id (Приходящий id может как повторяться, так и беспорядочно увеличиваться), создается объект точки, подвязывается сигнал на её удаление по сигналу. Затем заношу точку в вектор точек QVector vector_dot и из вектора добавляю на сцену последний добавленный в вектор элемент.
После того как у точки opacity возвращяется к 0 и вызывается сигнал на удаление, вызывается destroyDot() и *releaseObjResources(QVector vector) в котором из вектора удаляется первый элемент (так как таймер отрабатывает первым как раз у него), этот элемент удаляется со сцены и на этом все.

void My_Widget::createDot(float distance, float azimuth, int dot_id)
{
    if (is_window_show)
    {
        float diameter = 0;
        if (ui->graphicsView->width() > ui->graphicsView->height())
            diameter = ui->graphicsView->height() - 5;
        else if (ui->graphicsView->width() <= ui->graphicsView->height())
            diameter = ui->graphicsView->width() - 5;

        float x_pos = (diameter - _target_size) / 4 + (distance / _max_range / 1000 * diameter / 4) * sin(azimuth * M_PI / 180);
        float y_pos = (diameter - _target_size) / 4 + (distance / _max_range / 1000 * diameter / 4) * -1 * cos(azimuth * M_PI / 180);
        if (pow(x_pos, 2) + pow(y_pos, 2) > pow(diameter / 2, 2))
            return;

        int id_absolut = dot_id;
        int id_color = dot_id % 50;

        dot = new Dot(id_absolut, id_color, 10, 10, x_pos, y_pos);
        connect(dot, &Dot::destroy_dot, this, &My_Widget::destroyDot);
        vector_dot.append(dot);
        scene->addItem(vector_dot.last());
    }
}

void My_Widget::destroyDot()
{
    releaseObjResources(&vector_dot);
}

template<typename T>
void My_Widget::releaseObjResources(QVector<T> *vector)
{
    for (T item : std::as_const(*vector))
    {
        scene->removeItem(item);
        scene->setSceneRect(scene->itemsBoundingRect());
        vector->removeFirst();
        delete item;
        item = nullptr;
        break;
    }
}


Все работает стабильно пока кол-во объектов на сцене не привышает ~700 объектов, после то ли таймеры перегружают все, или еще что, но система начинает знатно тормозить, пропускаются сигналы на удаления объекта, и в целом они начинают появляться и исчезать медленнее, от чего сцена начинает многократно заполняться объектами, пока не повиснет вовсе... Подскажите на что обратить внимание для оптимизации данного кода.
Также пробовал использовать встроенный метод advanse(int phase), вместо таймера, но в любой момент когда не вызывается сигнал на удаление, программа вылетает.


Ответы (1 шт):

Автор решения: IvAries

Спасибо, Alexander Chernin, помог и пример и совет переработать архитектуру. Теперь все живет прекрасно объектов до ~30-40к, дальше не проверял)
Dot.h
Добавил объекту Q_PROPERTY для контроля его opacity, от таймеров избавился, больше нет нужды, всю реализацию также в ашку перенёс.

#include "control/my_color.h"

#include <QObject>
#include <QPen>
#include <QGraphicsEllipseItem>
#include <QTimer>

class Dot: public QObject, public QGraphicsEllipseItem
{
    Q_OBJECT
    Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
public:
    Dot(int id, int color_id, int width, int height, float x, float y, QGraphicsItem *parent = nullptr)
        : QGraphicsEllipseItem(x, y, width, height, parent)
    {
        setPos(x, y);
        setOpacity(0.0);

        if (id != -1)
            setDotColor(color_id);
        else
            setPen(QPen(My_Color::MainColor::White));        
    }

private:
    void setDotColor(int color_id)
    {
        if (color_id % 10 == 0)
        {
            setPen(QPen(My_Color::RedTones::Red));
            setBrush(QBrush(My_Color::RedTones::Red));
        }
        else if (color_id % 10 == 1)
        {
            setPen(QPen(My_Color::PinkTones::DeepPink));
            setBrush(QBrush(My_Color::PinkTones::DeepPink));
        }
        else if (color_id % 10 == 2)
        {
            setPen(QPen(My_Color::OrangeTones::OrangeRed));
            setBrush(QBrush(My_Color::OrangeTones::OrangeRed));
        }
        else if (color_id % 10 == 3)
        {
            setPen(QPen(My_Color::YellowTones::Yellow));
            setBrush(QBrush(My_Color::YellowTones::Yellow));
        }
        else if (color_id % 10 == 4)
        {
            setPen(QPen(My_Color::PurpleTones::Violet));
            setBrush(QBrush(My_Color::PurpleTones::Violet));
        }
        else if (color_id % 10 == 5)
        {
            setPen(QPen(My_Color::GreenTones::Lime));
            setBrush(QBrush(My_Color::GreenTones::Lime));
        }
        else if (color_id % 10 == 6)
        {
            setPen(QPen(My_Color::GreenTones::Green));
            setBrush(QBrush(My_Color::GreenTones::Green));
        }
        else if (color_id % 10 == 7)
        {
            setPen(QPen(My_Color::BlueTones::Aqua));
            setBrush(QBrush(My_Color::BlueTones::Aqua));
        }
        else if (color_id % 10 == 8)
        {
            setPen(QPen(My_Color::BlueTones::Blue));
            setBrush(QBrush(My_Color::BlueTones::Blue));
        }
        else if (color_id % 10 == 9)
        {
            setPen(QPen(My_Color::BlueTones::DodgerBlue));
            setBrush(QBrush(My_Color::BlueTones::DodgerBlue));
        }
    }
};

My_Widget.cpp Теперь после создания точки работаю с QPropertyAnimation, беру первый объект из вектора, ставлю сигнал на конец анимации появлении для запуска анимации исчезновения, по завершению последней удаляю объект, и теперь все идеально и без тормозов отрабатывает)))

void My_Widget::createDot(float distance, float azimuth, int dot_id)
{
    if (is_window_show)
    {
        float diameter = 0;
        if (ui->graphicsView->width() > ui->graphicsView->height())
            diameter = ui->graphicsView->height() - 5;
        else if (ui->graphicsView->width() <= ui->graphicsView->height())
            diameter = ui->graphicsView->width() - 5;

        float x_pos = (diameter - _dot_size) / 4 + (distance / _max_range / 1000 * diameter / 4) * sin(azimuth * M_PI / 180);
        float y_pos = (diameter - _dot_size) / 4 + (distance / _max_range / 1000 * diameter / 4) * -1 * cos(azimuth * M_PI / 180);

        if (pow(x_pos + 4 - (diameter + 6) / 4, 2) + pow(y_pos + 4 - (diameter + 6) / 4, 2) > pow((diameter - 5) / 4, 2))
            return;

        int dot_id_absolut = dot_id;
        int dot_id_color = dot_id % 50;

        dot = new Dot(dot_id_absolut, dot_id_color, _dot_size, _dot_size, x_pos, y_pos);
        vector_dot.append(dot);        
        scene->addItem(vector_dot.last());

        createObjectAnimation(&vector_dot, 500, 1000);
    }
}

template<typename T>
void My_Widget::createObjectAnimation(QVector<T> *vector, int appearence_duration, int disappearence_duration)
{
    T current_object = vector->takeFirst();
    QPropertyAnimation *animation_appearence = new QPropertyAnimation(current_object, "opacity");
    connect(animation_appearence, &QPropertyAnimation::finished, [=](){
        QPropertyAnimation *animation_disappearence = new QPropertyAnimation(current_object, "opacity");
        connect(animation_disappearence, &QPropertyAnimation::finished, [=](){
            scene->removeItem(current_object);
            current_object->deleteLater();
        });
        animation_disappearence->setDuration(disappearence_duration);
        animation_disappearence->setStartValue(1.0);
        animation_disappearence->setEndValue(0.0);
        animation_disappearence->setEasingCurve(QEasingCurve::InOutQuad);
        animation_disappearence->start();
    });
    animation_appearence->setDuration(appearence_duration);
    animation_appearence->setStartValue(0.0);
    animation_appearence->setEndValue(1.0);
    animation_appearence->setEasingCurve(QEasingCurve::InOutQuad);
    animation_appearence->start();
}

Проверял утечки и состояние массива, с этим теперь все тоже хорошо.

→ Ссылка