Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Ground от Июль 23, 2011, 08:22



Название: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 23, 2011, 08:22
Здравствуйте! Имеется QGraphicsScene, унаследованный QGraphicsView и виджет размерами 200х200, перерисовывающийся каждые 40мс. В QGraphicsView в качестве фона установлено SVG изображение (через drawBackground). Виджет добавлен в сцену через addWidget(). При такой структуре, при каждой перерисовке виджета вызывается еще и функция drawBackground, что очень сильно влияет на производительность. В чем может быть дело?

drawBackground
Код
C++ (Qt)
void graphicsView::drawBackground(QPainter *painter, const QRectF &rect)
{
        if (image.size() != viewport()->size())
{
image = QImage(viewport()->size(), QImage::Format_ARGB32_Premultiplied);
}
 
QPainter imagePainter(&image);
renderer->load(QString("map.svg"));
renderer->render(&imagePainter, rect);
imagePainter.end();
 
painter->drawImage(0, 0, image);
 
}


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 23, 2011, 11:07
Постоянно грузишь и отрисовываешь svg картинку.

Мухи отдельно, котлеты отдельно: я бы загрузил только один раз, например в конструкторе, в drawBackground рисовал бы только image, а изменение размера вынес бы в QGraphicsView::resizeEvent, примерно так:

Код
C++ (Qt)
QGraphicsView::QGraphicsView()
{
renderer->load(QString("map.svg"));
}
 
void QGraphicsView::resizeEvent(QResizeEvent *event)
{
image = QImage(viewport()->size(), QImage::Format_ARGB32_Premultiplied);
 
QPainter imagePainter(&image);
renderer->render(&imagePainter, rect);
imagePainter.end();
}
 
void graphicsView::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->drawImage(0, 0, image);
}
 


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 23, 2011, 11:31
Способ помог, спасибо. Только теперь еще два вопроса появилось.
Первый: Значит перерисовка фона каждый раз, когда изменяется какой-либо элемент из QGraphicsScene - это нормальное явление?
И второй:
Код
C++ (Qt)
void QGraphicsView::resizeEvent(QResizeEvent *event)
{
image = QImage(viewport()->size(), QImage::Format_ARGB32_Premultiplied);
 
QPainter imagePainter(&image);
renderer->render(&imagePainter, rect);
imagePainter.end();
}
Вот тут в строке renderer->render(...) вторым параметром требуется получить область из SVG изображения, которую нужно отрендерить. Как эту область получить, с учетом зуминга, смещения скроллбаров и.т.д?


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 23, 2011, 12:10
да, явление нормальное, т.к. перерисовка элемента затрагивает фон

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


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 23, 2011, 12:19
честно говоря тогда непонятно, для чего отрисовывать svg-изображение на картинку, но только часть его, а потом рисовать картинку фоном
Можно тогда рисовать svg сразу в фон, без промежуточных кеширований, либо отлавливать изменения текущей области видимости и кешировать нужную область svg-картинки.

А каким образом отрисовывать SVG сразу в фон? Посмотрел примеры (SVG Viewer, в частности), поискал в интернете - везде сначала преобразуют SVG в QImage, а после - делают фоном.
Был еще вариант сделать SVG-картинку QGraphicsSvgItem и добавить в сцену спомощью addItem, но такая реализация точно так же тормозит.


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 23, 2011, 12:21
Отрисовать сразу:

Код
C++ (Qt)
void graphicsView::drawBackground(QPainter *painter, const QRectF &rect)
{
renderer->render(painter, rect);
}


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 23, 2011, 13:09
Теперь появились проблемы:
1. Фоновое изображение становится фоном не только для QGraphicsView, но и для дочернего виджета.
2. Фоновая картинка теперь масштабируется в соответствии с размерами окна. Если окно 500х500, а картинка 3000х3000, то она ресайзится до 500х500 и пропадает возможность ее скейлить. При скейле элементы меняют размер, а фоновое изображение - нет.
Если же отрисовать сразу, но всю картинку, то будут тормоза:
Код
C++ (Qt)
void graphicsView::drawBackground(QPainter *painter, const QRectF &rect)
{
renderer->render(painter, QRectF(0, 0, 2700, 2000));
}
 


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 23, 2011, 15:12
Уже начинаю склоняться к мысли, что эти тормоза - особенность самой технологии SVG через QPainter. В примере SVG Viewer рисование грузит систему аналогичным образом. Но стоит переключиться в режим OpenGL - загрузка процессора падает до 0.


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 23, 2011, 18:52
Тормоза зависят от сложности svg-изображения.
через OpenGL рисует с аппаратным ускорением, поэтому и быстрее.

Выложил бы минимальный проект с этой проблемой, было бы проще.


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 24, 2011, 04:28
Выложил бы минимальный проект с этой проблемой, было бы проще.

Написал тестовый виджет, который только перерисовывается раз в 40мс.

TestWidget:
Код
C++ (Qt)
class testWidget : public QWidget
{
Q_OBJECT
 
public:
testWidget(QWidget* parent = 0);
 
 
protected:
virtual void paintEvent(QPaintEvent *);
};
 
 
testWidget::testWidget(QWidget *parent) :
QWidget(parent)
{
resize(200, 200);
 
QTimer* processTimer = new QTimer();
processTimer->setInterval(40);
connect(processTimer, SIGNAL(timeout()), this, SLOT(update()));
processTimer->start();
 
}
 
void testWidget::paintEvent(QPaintEvent *)
{
QPainter p(this);
static int i = 0;
i+=1;
p.setBrush((QColor)i);
p.drawRect(0, 0, 200, 200);
 
return;
}
 

MainWindow:
Код
C++ (Qt)
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(500, 500);
// Главный виджет
wgtMain = new QWidget(this);
wgtMain->setBackgroundRole(QPalette::Base);
          // SVG-карта
map = new QGraphicsSvgItem("map.svg");
map->setFlags(QGraphicsItem::ItemClipsToShape);
map->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
map->setZValue(0);
          // Тестовый виджет
test = new testWidget;
// Графическая сцена
scene = new QGraphicsScene(wgtMain);
scene->addItem(map);
scene->addWidget(test);
// Область отображения для сцены
scene->setSceneRect(map->boundingRect());
 
lblBackgroundImage = new QGraphicsView(scene, wgtMain);
          // Область просмотра сменил на OGL-виджет, иначе загрузка процессора была под 100%. Сейчас 50%
lblBackgroundImage->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
lblBackgroundImage->setGeometry(0, 55, width(), height() - 55);
lblBackgroundImage->setDragMode(QGraphicsView::ScrollHandDrag);
lblBackgroundImage->setRenderHint(QPainter::Antialiasing);
 
setCentralWidget(wgtMain);
}



Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 24, 2011, 11:04
Имелось ввиду проект прикрепить как вложение, с svg-картинкой, чтобы собрать и запустить.


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 24, 2011, 11:34
Имелось ввиду проект прикрепить как вложение, с svg-картинкой, чтобы собрать и запустить.
Прикрепил. Только SVG и обновляющийся виджет, ничего лишнего


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 24, 2011, 12:32
У меня помогла отрисовка svg-картинки в виде фона, с флагом QGraphicsView::CacheBackground:

Код
C++ (Qt)
graphicsView::graphicsView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent)
{
setCacheMode(QGraphicsView::CacheBackground);
 
renderer = new QSvgRenderer(this);
renderer->load(QString("map.svg"));
 
setSceneRect(renderer->viewBoxF());
}
 
void graphicsView::drawBackground(QPainter* painter, const QRectF&)
{
renderer->render(painter, renderer->viewBoxF());
}

Проект во вложении


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Июль 24, 2011, 12:56
У меня помогла отрисовка svg-картинки в виде фона, с флагом QGraphicsView::CacheBackground:

Вот оказывается, в чем был подвох... Огромное спасибо, это то что нужно!


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: _OLEGator_ от Июль 24, 2011, 23:07
не за что, просто надо документацию лучше и внимательнее читать =)
базовый функционал обычно реализован и велосипеды изобретать не приходится


Название: Re: QGraphicsScene (фон + виджет поверх). Тормоза
Отправлено: Ground от Сентябрь 08, 2011, 13:29
И снова здравствуйте! Подниму свою тему, т.к. вопрос практически аналогичный.
Цитировать
Имеется QGraphicsScene, унаследованный QGraphicsView и небольшой виджет, перерисовывающийся каждые 40мс. В QGraphicsView в качестве фона установлено SVG изображение (через drawBackground). Виджет добавлен в сцену через addWidget().

Вот код унаследованного QGraphicsView:
Код
C++ (Qt)
graphicsView::graphicsView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent)
{
setRenderHint(QPainter::Antialiasing);
//setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
setCacheMode(QGraphicsView::CacheBackground);
 
renderer = new QSvgRenderer(this);
renderer->load(QString("map.svg"));
 
setSceneRect(renderer->viewBoxF());
}
 
void graphicsView::drawBackground(QPainter* painter, const QRectF&)
{
renderer->render(painter, renderer->viewBoxF());
}

И проблема сейчас такого рода: при разворачивании QGraphicsView на весь экран и включенной опции setDragMode(QGraphicsView::ScrollHandDrag) - изображение двигается рывками, причем очень заметными. Проблема решается с помощью setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))), но в этом случае значительно нагружается процессор. Как можно побороть эти проблемы?

Минимальный проект с представленной проблемой прилагаю. Проблема наблюдается при разрешении 1280х1024 и выше.