Оптимизация метода рисования painter
Версия qt4.7.8
Есть у меня сцена размером 544x1104 на сцене рисуются прямоугольники одинаковых размеров. Внутри прямоугольника есть еще один прямоугольник меньшего размера. Вместе они образуют прямоугольник с рамкой. Цвет рамки и внутренней области разные. Каждый прямоугольник имеет свой номер. Номер никогда не изменяется. Размеры прямоугольника никогда не изменяются. Координаты никогда не изменяются. Изменяется только цвет рамки и внутренней области. Использую достаточно слабый пк и перемотка сцены очень сильно нагружают цп до 100%. Меня это не устраивает.
Написал следующий код:
#ifndef STATEBALANSER_H
#define STATEBALANSER_H
#include <QGraphicsRectItem>
#include <QPainter>
#include <memorymap.h>
#include <QFont>
#include <QFontDatabase>
class StateBalanser : public QGraphicsRectItem
{
public:
StateBalanser(int num);
public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void StateBalance();
void setColorState(unsigned char value_1, unsigned char value_2);
void setColorText(QColor text);
void setFontText(QFont fontText);
private:
int number;
private:
QPainterPath _painterPath;
QRectF _intersect;
QColor _fillColorState;
QColor _fillColorModeWork;
QColor _fillColorText;
QFont _fontText;
public:
QRectF boundingRect() const;
QPainterPath shape() const;
};
#endif // STATEBALANSER_H
.cpp
#include "statebalanser.h"
StateBalanser::StateBalanser(int num) :
number(num)
{
setColorState(DisabledBalancer,StateDisabled);
StateBalanser::StateBalance();
StateBalanser::setColorText(Qt::black);
int id = QFontDatabase::addApplicationFont("/usr/share/fonts/liquidcr.ttf");
QFont font = QFont(QFontDatabase::applicationFontFamilies(id).first());
font.setPointSize(12);
StateBalanser::setFontText(font);
_painterPath = shape();
}
void StateBalanser::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->fillRect(boundingRect(), _fillColorModeWork);
painter->fillRect(_intersect, _fillColorState);
painter->drawPath(_painterPath);
painter->setFont(_fontText);
painter->setPen(_fillColorText);
painter->drawText(boundingRect(), Qt::AlignCenter, QString().sprintf("%03d", number + 1) );
}
void StateBalanser::StateBalance()
{
_intersect = boundingRect().intersected(QRectF(3, 3, 44, 32));
}
void StateBalanser::setColorState(unsigned char value_1, unsigned char value_2)
{
switch(value_2)
{
case StopBalancer : _fillColorModeWork = QColor("#000000") ;
break;
case ChargeBalancer : _fillColorModeWork = QColor("#0000ff") ;
break;
case DischargeBalancer : _fillColorModeWork = QColor("#ff0000") ;
break;
case EsrBalancer : _fillColorModeWork = QColor("#32CD32") ;
break;
case DisconnectBalancer : _fillColorModeWork = QColor("#000000") ;
break;
case DisabledBalancer : _fillColorModeWork = QColor("#ffffff") ;
break;
default : _fillColorModeWork = QColor("#ffffff") ;
break;
}
switch(value_1)
{
case StateGood : _fillColorState = QColor("#99FF99") ;
break;
case StateFiftyFifty: _fillColorState = QColor("#FFFF66") ;
break;
case StateFault : _fillColorState = QColor("#FF9999") ;
break;
case StateDisconnect: _fillColorState = QColor("#999999") ;
break;
case StateMeasurement: _fillColorState = QColor("#a2c8d6") ;
break;
case StateDisabled: _fillColorState = QColor("#ffffff") ;
break;
default : _fillColorState = QColor("#ffffff") ;
break;
}
}
void StateBalanser::setColorText(QColor text)
{
_fillColorText = QColor(text);
}
void StateBalanser::setFontText(QFont fontText)
{
_fontText = fontText;
}
QRectF StateBalanser::boundingRect() const
{
return QRectF(0, 0, 49, 37);
}
QPainterPath StateBalanser::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
Если убрать методы
painter->drawPath(_painterPath);
painter->setFont(_fontText);
painter->setPen(_fillColorText);
painter->drawText(boundingRect(), Qt::AlignCenter, QString().sprintf("%03d", number + 1) );
То загрузка цп падает до 60%. И данные параметры никогда не изменяются. Но как можно убрать данные методы из paint при этом не нарушив нумерацию, цвет, шрифт? Допустим один раз проинициализировать в конструкторе.
Ответы (1 шт):
Можно завести на каждый параметр флаг-индикатор изменения. Когда вы изменяете какой-либо параметр, то флаг устанавливаете в "Изменен", а при отрисовке учитываете это. Например так:
void setChangeParam(<Class..> new_value) {
this->some_value = new_value;
this->some_value_changed = true;
}
В методе отрисовки:
void paint(...) {
...
if(some_value_changed) {
painter->set(this->some_value);
// Больше не отрисовывать
some_value_changed = false;
}
...
}
Но, у вас имеется сцена с одинаковыми элементами, у которых одинаковые параметры отрисовки, а объект painter поступает в "готовом виде" извне, тогда рекомендую наследоваться от QGraphicsView и создать методы "массового" изменения параметров.
Следите за руками:
class MyGraphicsView : public QGraphicsView {
public:
MyGraphicsView(...):
...,
_myScene(new MyGraphicsScene()),
_stateBalancerFontChanged(false)
{
// MyGraphicsScene описан после класса MyGraphicsView
setScene(_myScene.get());
}
public:
setStateBalancerFont(const QFont& font) {
_stateBalancerFont = font;
_stateBalancerFontChanged = true;
update();
}
private:
QScopedPoint<MyGraphicsScene> _myScene;
QScopedPoint<QPainter> _painter;
bool _stateBalancerFontChanged;
protected:
void paintEvent(QPaintEvent* event) {
// QPainter можно создать только тогда, когда будет готово
// устройство для отрисовки, а это метод paintEvent
// поэтому проверяем, что паинтер еще не создан и создаем его
if( _painter.isNull() ) {
_painter.reset(new QPainter(viewport())); // здесь вызван именно viewport()!
}
// Если painter все еще null, то у нас проблемы
if( _painter.isNull() )
return;
// А если нет, то все ок и мы можем рисовать:
// подложка
this->drawBackground(_painter.get(), viewport().rect());
// все остальное рисуем сами
if( _stateBalancerFontChanged ) {
// Устанавливаем измененное значение
_painter.get()->setFont(_stateBalancerFont);
_stateBalancerFontChanged = false;
}
for(QGraphicsItem* item : _myScene.get()->stateBalancerItems()) {
item->paint(_painter.get(), /*option*/, widget)
}
// Далее устанавливаем, ЕСЛИ ЕСТЬ, параметры для других
// элементов сцены по той же схеме,
// а потом перебираем эти элементы в цикле и отрисовываем
// Можно вызвать отрисовку морды, а можно не вызывать
this->drawForeground(_painter.get(), viewport().rect());
}
}
Соответственно, класс сцены должен иметь методы выдачи элементов сцены по типам (если таковые есть, конечно):
class MyGraphicsScene: ... {
public:
QList<StateBalancer*>& stateBalancerItems() {
return ...
}
QList<ItemType1*>& itemsType1() {
return ...
}
...
QList<ItemTypeN*> itemsTypeN() {
return ...
}
}
После всех этих манипуляций, из метода отрисовки StateBalancer можно исключить следующее:
painter->setFont(_fontText);
painter->setPen(_fillColorText);
и тому подобное, а кроме того, все поля, которые мы устанавливаем "глобально". Ключевая идея, надеюсь ясна.
Кроме того, обратите внимание на метод:
QPainterPath StateBalanser::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
Он постоянно создает QPainterPath, в который добавляется один и тот же неизменяемый boundingRect(), следовательно, такие методы тоже можно оптимизировать.