Russian Qt Forum
Ноябрь 24, 2024, 01:36 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: QGraphicsScene (фон + виджет поверх). Тормоза  (Прочитано 14393 раз)
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);
 
}
Записан
_OLEGator_
Гость
« Ответ #1 : Июль 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);
}
 
Записан
Ground
Гость
« Ответ #2 : Июль 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 изображения, которую нужно отрендерить. Как эту область получить, с учетом зуминга, смещения скроллбаров и.т.д?
Записан
_OLEGator_
Гость
« Ответ #3 : Июль 23, 2011, 12:10 »

да, явление нормальное, т.к. перерисовка элемента затрагивает фон

честно говоря тогда непонятно, для чего отрисовывать svg-изображение на картинку, но только часть его, а потом рисовать картинку фоном
Можно тогда рисовать svg сразу в фон, без промежуточных кеширований, либо отлавливать изменения текущей области видимости и кешировать нужную область svg-картинки.
Записан
Ground
Гость
« Ответ #4 : Июль 23, 2011, 12:19 »

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

А каким образом отрисовывать SVG сразу в фон? Посмотрел примеры (SVG Viewer, в частности), поискал в интернете - везде сначала преобразуют SVG в QImage, а после - делают фоном.
Был еще вариант сделать SVG-картинку QGraphicsSvgItem и добавить в сцену спомощью addItem, но такая реализация точно так же тормозит.
Записан
_OLEGator_
Гость
« Ответ #5 : Июль 23, 2011, 12:21 »

Отрисовать сразу:

Код
C++ (Qt)
void graphicsView::drawBackground(QPainter *painter, const QRectF &rect)
{
renderer->render(painter, rect);
}
Записан
Ground
Гость
« Ответ #6 : Июль 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));
}
 
Записан
Ground
Гость
« Ответ #7 : Июль 23, 2011, 15:12 »

Уже начинаю склоняться к мысли, что эти тормоза - особенность самой технологии SVG через QPainter. В примере SVG Viewer рисование грузит систему аналогичным образом. Но стоит переключиться в режим OpenGL - загрузка процессора падает до 0.
Записан
_OLEGator_
Гость
« Ответ #8 : Июль 23, 2011, 18:52 »

Тормоза зависят от сложности svg-изображения.
через OpenGL рисует с аппаратным ускорением, поэтому и быстрее.

Выложил бы минимальный проект с этой проблемой, было бы проще.
Записан
Ground
Гость
« Ответ #9 : Июль 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);
}

« Последнее редактирование: Июль 24, 2011, 04:30 от Ground » Записан
_OLEGator_
Гость
« Ответ #10 : Июль 24, 2011, 11:04 »

Имелось ввиду проект прикрепить как вложение, с svg-картинкой, чтобы собрать и запустить.
Записан
Ground
Гость
« Ответ #11 : Июль 24, 2011, 11:34 »

Имелось ввиду проект прикрепить как вложение, с svg-картинкой, чтобы собрать и запустить.
Прикрепил. Только SVG и обновляющийся виджет, ничего лишнего
Записан
_OLEGator_
Гость
« Ответ #12 : Июль 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());
}

Проект во вложении
Записан
Ground
Гость
« Ответ #13 : Июль 24, 2011, 12:56 »

У меня помогла отрисовка svg-картинки в виде фона, с флагом QGraphicsView::CacheBackground:

Вот оказывается, в чем был подвох... Огромное спасибо, это то что нужно!
Записан
_OLEGator_
Гость
« Ответ #14 : Июль 24, 2011, 23:07 »

не за что, просто надо документацию лучше и внимательнее читать =)
базовый функционал обычно реализован и велосипеды изобретать не приходится
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.492 секунд. Запросов: 20.