Сохранение и восстановления состояния QPainter - АлтунинВВ.Блог - всё об IT-технологиях!
Суббота, 03 октября 2020 19:00

Сохранение и восстановления состояния QPainter

Россия

Сегодня мы рассмотрим функционал QPainter, который является очень важным для эффективного применения данной библиотеки – это сохранение и восстановление состояния.

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

Так как у нас места у нас мало, увеличим размер окна до 400х400

Сделать это вы можете в редакторе форм QTCreator.

Работать мы будем с кодом из первой части:

Удалим методы

    void drawChildPicAbs();
    void drawChildPicRel();

они нам не понадобятся.

Давайте разберемся, как работают методы save() и restore()

При вызове painter->save() происходит сохранение всех доступных настроек в о внутренний стэк.

Что такое стэк? Как правило, это область памяти, в которой объекты храниться в виде списка. Основной особенностью стэка является способ доступа к элементам списка. Вы всегда можете извлечь из стека только тот элемент, который был помещен в него последним. В общем стек работает как детская игрушка – пирамидка. На стержень можно надевать кольца, но снимать их можно строго последовательно.

Итак, когда мы вызываем save() текущее состояние помещается в стэк.

Когда нам нужно восстановить состояние мы вызываем restore(). При этом текущее состояние QPainter будет уничтожено!

Если в стеке остался один элемент, то будет восстановлено состояние этого элемента, неважно, сколько раз мы будем вызывать метод restore().

Давайте проверим это на примере.

Нарисуем несколько колец в квадрате, для этого добавим метод:

void MainWindow::drawRings()
{
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    apen.setWidth(3);
    painter->setPen(apen);

    painter->translate(QPoint(100,100));

    painter->drawRect(QRect(0,0,250,250));

    painter->translate(QPoint(50,50));

    apen.setWidth(1);
    painter->setPen(apen);
    painter->drawEllipse(0, 0, 30,30);
    painter->drawEllipse(-10, 0, 30,30);
    painter->drawEllipse(10, 0, 30,30);
    painter->drawEllipse(0, -10, 30,30);
    painter->drawEllipse(0, 10, 30,30);


    painter->translate(QPoint(100,0));

    painter->drawEllipse(0, 0, 30,30);
    painter->drawEllipse(-10, 0, 30,30);
    painter->drawEllipse(10, 0, 30,30);
    painter->drawEllipse(0, -10, 30,30);
    painter->drawEllipse(0, 10, 30,30);


    painter->translate(QPoint(0,100));

    painter->drawEllipse(0, 0, 30,30);
    painter->drawEllipse(-10, 0, 30,30);
    painter->drawEllipse(10, 0, 30,30);
    painter->drawEllipse(0, -10, 30,30);
    painter->drawEllipse(0, 10, 30,30);


    painter->translate(QPoint(-100,0));

    painter->drawEllipse(0, 0, 30,30);
    painter->drawEllipse(-10, 0, 30,30);
    painter->drawEllipse(10, 0, 30,30);
    painter->drawEllipse(0, -10, 30,30);
    painter->drawEllipse(0, 10, 30,30);
}

У нас получился такой рисунок:

2020-10-01_15-47-35.png

Стрелками я показал путь, по которому производилась трансляция.

Теперь если мне потребуется разместить ещё один орнамент, в центре, мне придется вычислять новое смещение, что не очень удобно, а если я захочу заполнить весь квадрат орнаментом, придется еще больше усложнять алгоритм.

А теперь давайте упростим метод.

Добавим отдельный метод для отрисовки орнамента

void MainWindow::drawOrnament(QPoint pos, QColor color)
{
    painter->save();
    QPen apen = QPen(color);
    apen.setWidth(1);
    painter->setPen(apen);

    painter->translate(pos);

    painter->drawEllipse(0, 0, 30,30);
    painter->drawEllipse(-10, 0, 30,30);
    painter->drawEllipse(10, 0, 30,30);
    painter->drawEllipse(0, -10, 30,30);
    painter->drawEllipse(0, 10, 30,30);
    painter->restore();
}

Изменим метод drawRings()

void MainWindow::drawRings()
{
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    apen.setWidth(3);
    painter->setPen(apen);

    painter->translate(QPoint(100,100));
    painter->drawRect(QRect(0,0,250,250));

    drawOrnament(QPoint(50,50),linesColor);
    drawOrnament(QPoint(50,100),linesColor);
    drawOrnament(QPoint(100,50),linesColor);
    drawOrnament(QPoint(100,100),linesColor);
    drawOrnament(QPoint(150,50),linesColor);
    drawOrnament(QPoint(150,100),linesColor);
}

 

У нас получился рисунок:

2020-10-01_15-57-45.png

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

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

void MainWindow::drawRings()
{
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    apen.setWidth(3);
    painter->setPen(apen);

    painter->translate(QPoint(100,100));
    painter->drawRect(QRect(0,0,240,240));

    for (int i = 0; i < 7; i++) {
        for (int j = 0; j < 7; j++) {
            QColor linesColor( QRandomGenerator::global()->bounded(240),
                               QRandomGenerator::global()->bounded(240),
                               QRandomGenerator::global()->bounded(240),
                               255);

            drawOrnament(QPoint(15+30*i,15+30*j),linesColor);
        }
    }

}

 У нас получиться

2020-10-01_16-09-55.png

Заключение

Сегодня мы рассмотрели методы save() И restore() класса QPainter.

Мы разобрались, как они работают и написали небольшой тестовый проект, чтобы продемонстрировать работу методов save() и restore().

В следующей части мы рассмотрим новый тип трансформации – масштабирование.

Прочитано 124 раз Последнее изменение Пятница, 02 октября 2020 14:34