QGraphicsView медленный с большим количеством QGraphicsPixmapItem

Я сделал небольшой код для тестирования возможностей QGraphicsView с использованием QtCreator.

Код довольно прост, только что создал класс, унаследованный от QGraphicsView, на котором есть QGraphicsScene. Заполните сцену множеством QGraphicsPixmapItem (в данном случае 2000), масштабированных до 100×100, и поместите их случайным образом в сцену.

Затем внутри пользовательского класса и с помощью QTimer переместите немного все элементы сцены.

(Добавлен второй QTimer, чтобы увидеть, сколько раз в секунду вызывается первый QTimer).

Он отлично работает с несколькими сотнями элементов, но если количество элементов увеличивается, производительность падает.

Может ли кто-нибудь дать мне подсказку о том, как повысить производительность?
Может быть, доступ к элементам с использованием QList медленный …
или использование QTimer для этой простой анимации — очень плохая идея …
или просто нужно добавить несколько флагов оптимизации …
может быть, забыли QGraphicsView и попробуйте QtQuick и QML …

Конечное приложение должно рисовать много изображений на экране и анимировать некоторые из них, используя png изображения в качестве источника.

test.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = test
TEMPLATE = appSOURCES += main.cpp \
c_view.cpp

HEADERS  += \
c_view.h

FORMS    +=

RESOURCES += \
res.qrc

C_View.h

#ifndef C_VIEW_H
#define C_VIEW_H

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QTimer>

class C_View : public QGraphicsView
{
Q_OBJECT

public:
C_View();

QGraphicsScene *scene;

QGraphicsTextItem *label_fps;

QTimer timer;
QTimer timer_fps;
int interval=0;
int fps=0;
private slots:
void random_move();
void show_fps();
};

#endif // C_VIEW_H

C_View.cpp

#include "c_view.h"
C_View::C_View()
{
this->scene = new QGraphicsScene();
this->setScene(this->scene);

// Label to see how many times per seconds the random_move function gets called
this->label_fps=new QGraphicsTextItem();
this->label_fps->setDefaultTextColor(Qt::black);
this->label_fps->setFont(QFont("times",16));
this->label_fps->setPos(10,10);
this->scene->addItem(this->label_fps);

// Qtimer to enter random_move function
connect(&this->timer,SIGNAL(timeout()),this,SLOT(random_move()));
//this->interval=10; // 100 FPS?
this->interval=25; // 40 FPS?
//this->interval=50; // 20 FPS?
//this->interval=100; // 10 FPS?
this->timer.setInterval(this->interval);
this->timer.start();

// QTimer to update the FPS label
connect(&this->timer_fps,SIGNAL(timeout()),this,SLOT(show_fps()));
this->timer_fps.setInterval(1000); // Once a second
this->timer_fps.start();

}

// Funcion that moves a bit all the items of the scene
void C_View::random_move()
{
QList <QGraphicsItem*> l = this->items();
int ini=0;
for(int i=ini;i<l.size();i++)
{
l[i]->setPos(l[i]->x()+(rand()%3)-1,l[i]->y()+(rand()%3)-1);
}

this->fps++;
}

// Just show how many times random_move function gets call, since last time
void C_View::show_fps()
{
this->label_fps->setPlainText("FPS "+QString::number(this->fps));
this->label_fps->setZValue(1);
this->fps=0;
}

main.cpp

#include <QApplication>

#include "c_view.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);

C_View *view = new C_View();

// Fill the QGraphicsView with lots of images
for(int i=0;i<2000;i++)
{
QGraphicsPixmapItem *item=new QGraphicsPixmapItem();
item->setPixmap(QPixmap(":/images/p.png").scaled(100,100));
item->setPos(rand()%view->width(),rand()%view->height());
view->scene->addItem(item);
}

view->show();

return a.exec();
}

1

Решение

По моему мнению, предложение отказаться от Qt немного фаталистично и преждевременно. Наше приложение использует QGraphicsScene со многими десятками тысяч элементов, и мы имеем хорошую производительность. Однако большинство из них не являются изображениями, и мы не перемещаем тысячи их одновременно. Переключение на что-то другое может в конечном итоге иметь место, но сначала стоит провести дополнительные эксперименты.

Профилировщик, если он у вас есть, безусловно, может вам помочь. Мы используем Visual Studio, и там хорошо работает профилировщик, но я не знаю, есть ли у Qt Creator какая-либо возможность профилирования. Ваш пример приложения очень прост, и я не вижу ничего очевидного для изменения, но профилирование часто очень показательно.

Поскольку вы перемещаете объекты по сцене, поэкспериментируйте с параметрами QGraphicsScene :: setItemIndexMethod и QGraphicsScene :: setBspTreeDepth. Рекомендуемые настройки для них различаются в зависимости от того, как вы склонны использовать сцену.

По моему мнению, перемещение 2000 предметов каждую секунду кажется большим. Вы говорите, что в вашем окончательном приложении много изображений, но только некоторые из них движутся. Вы ожидаете, что 2000 из них переедут?

Кроме того, какую версию Qt вы используете? Более поздние версии принимают более правильные решения относительно движка рендеринга, и наш опыт показывает, что это довольно хорошо.

2

Другие решения

Запустите таймер после того, как ваш вид появится (например, в основной функции). Это должно исправить это странное поведение. Редактировать: Нет, это ничего не изменит. Пытаться

setViewportUpdateMode(BoundingRectViewportUpdate);
//setCacheMode(QGraphicsView::CacheBackground);

вместо. Вот документы: setViewportUpdateMode | setCacheMode. Не забудьте прочитать об этих типах аргументов функций.

С другой стороны, вам обычно не нужно обрабатывать указатель на QGraphicsScene внутри QGraphicsView, так как этот класс предоставляет вам функцию scene (). Если вашему коду нужен этот указатель, я думаю, что присвоение имени вашему атрибуту m_scene (или как угодно, кроме имени функции scene ()) будет более удобным.

Edit2

Еще один способ улучшить скорость рендеринга — уменьшить ограничивающий прямоугольник элемента сцены. Для этого вы можете масштабировать элементы растрового изображения до 50 * 50 (например) вместо 100 * 100. Действительно, чем меньше элементов сцены сталкиваются, тем быстрее будет процесс рендеринга Qt. Кроме того, вот статья, в которой описаны некоторые приемы оптимизации при использовании настроенного QGraphicsItem внутри QGraphicsView: Qt: Улучшение производительности QGraphicsView. Вы в настоящее время не используете настроенные предметы, но чтение этой статьи было бы полезно, я думаю.

0

По вопросам рекламы [email protected]