Масштабирование. Рисование в Qt. Трансформации. Часть 4
В прошлой части мы рассмотрели вращение. Сегодня мы рассмотрим новый тип трансформации – масштабирование. Мы продолжим работать с проектом с прошлой части.
Для начала, обеспечим себя чистым рабочим столом.
В методе
void MainWindow::paintEvent(QPaintEvent *)
закомментируем
drawChildPicRel();
В настройках формы главного окна изменим его размер до 400х400
Запустим, получим чистое окно:
Мы будем рисовать облака разного размера. Облако будет состоять из кругов.
Добавим метод для отрисовки облака.
void MainWindow::drawCloud()
{
QColor linesColor(0, 0, 255, 255);
QPen apen = QPen(linesColor);
painter->translate(QPoint(150,150));
painter->setBrush(Qt::white);
//
apen = QPen(QColor("white"));
apen.setWidth(3);
painter->setPen(apen);
painter->drawArc(0,0,30,30,360*16,360*16);
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(20,-10));
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(20,0));
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(20,10));
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(-20,10));
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(-20,0));
painter->drawEllipse(0, 0, 30,30);
}
Запустим, у нас получится:
Закомментируем строку
painter→setBrush(Qt::white);
Запустим еще раз:
Как видите, облако состоим из кругов, которые накладываются друг на друга, а так как они одинакового цвета, то переходов между ними не видно.
Мы и дальше будем использовать такой режим, для наглядности.
Наше облако выглядит неплохо, но несколько «синтетически». Давайте сделаем его менее симметричным.
Изменим код таким образом:
void MainWindow::drawCloud()
{
QColor linesColor(0, 0, 255, 255);
QPen apen = QPen(linesColor);
painter->translate(QPoint(150,150));
painter->setBrush(Qt::white);
apen = QPen(QColor("white"));
apen.setWidth(3);
painter->setPen(apen);
painter->drawEllipse(0, 0, 40,40);
painter->translate(QPoint(20,-10));
painter->drawEllipse(0, 0, 35,35);
painter->translate(QPoint(20,0));
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(20,10));
painter->drawEllipse(0, 0, 35,35);
painter->translate(QPoint(-20,10));
painter->drawEllipse(0, 0, 30,30);
painter->translate(QPoint(-20,0));
painter->drawEllipse(0, 0, 35,35);
}
У нас получиться:
или
Намного лучше, но нам нужны облака разных типов. Для этого усовершенствуем наш код.
void MainWindow::drawCloud(int seed)
{
QRandomGenerator prng(seed);
QColor linesColor(0, 0, 255, 255);
QPen apen = QPen(linesColor);
painter->translate(QPoint(150,150));
painter->setBrush(Qt::white);
//
apen = QPen(QColor("white"));
apen.setWidth(3);
painter->setPen(apen);
painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
painter->translate(QPoint(20,-10));
painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
painter->translate(QPoint(20,0));
painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
painter->translate(QPoint(20,10));
painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
painter->translate(QPoint(-20,10));
painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
painter->translate(QPoint(-20,0));
painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
}
Изменим вызов нашего метода на
drawCloud(123);
Запустим, получим:
или
Давайте разберемся в коде.
Прежде всего, для создания облака используется генератор случайных чисел, но так как нам нужно, чтобы облака были одной и той же формы, каждый раз при запуске программы мы используем зерно (seed) для инициализации генератора случайных чисел.
Это позволяет каждый раз получать от генератора одну и туже последовательность чисел, что очень удобно, так как позволяет установить строгую зависимость результата генерации от некого числа!
Далее при отрисовке каждого эллипса используется вызов метода, который генерирует случайное число в диапазоне от 0 до 10.
prng.bounded(10)
Таким образом у нас получаются отлично выглядящие облака!
Нарисуем несколько облаков.
void MainWindow::drawClouds()
{
painter->translate(QPoint(0,20));
painter->save();
painter->translate(QPoint(20,20));
drawCloud(121);
painter->restore();
painter->save();
painter->translate(QPoint(120,40));
drawCloud(122);
painter->restore();
painter->save();
painter->translate(QPoint(220,70));
drawCloud(123);
painter->restore();
painter->save();
painter->translate(QPoint(200,2));
drawCloud(124);
painter->restore();
}
Запустим:
Красивые облака это, конечно, хорошо, но облака бывают разными по размеру, давайте нарисуем несколько облаков разного размера, но, прежде чем продолжить, обновим код рисования домика, радуги и солнышка.
void MainWindow::drawChildPicRel()
{
painter->translate(QPoint(50,70));
painter->save();
QColor linesColor(0, 0, 255, 255);
QPen apen = QPen(linesColor);
apen.setWidth(3);
painter->setPen(apen);
painter->translate(QPoint(150,220));
//Квадрат - дом
painter->drawRect(-50,-50,100,100);
//Квадрат - окно
painter->drawRect(-30,-30,60,60);
//Полигон - крыша и дымоход
static const QPointF points[7] = {
QPointF(-50.0, -50.0),
QPointF(0.0, -100.0),
QPointF(20.0, -80.0),
QPointF(20.0, -110.0),
QPointF(30.0, -110.0),
QPointF(30.0, -70.0),
QPointF(50.0, -50.0)
};
painter->drawConvexPolygon(points, 7);
//Линия - крышка дымохода
painter->drawLine(QLine(15,-110,35,-110));
painter->drawLine(QLine(15,-110,25,-120));
painter->drawLine(QLine(25,-120,35,-110));
painter->restore();
painter->translate(QPoint(50,50));
int rainbowWidth = 200;
int rainbowHeight = 200;
int rainbowInRad = 30 * 16;
int rainbowOutRad = 120 * 16;
int rainbowArcHeight = 4;
//Дуга - Радуга
apen = QPen(QColor("red"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,0,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
apen.setWidth(rainbowArcHeight);
apen = QPen(QColor("orange"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,rainbowArcHeight,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
apen = QPen(QColor("yellow"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,rainbowArcHeight*2,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
apen = QPen(QColor("green"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,rainbowArcHeight*3,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
apen = QPen(QColor("lightblue"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,rainbowArcHeight*4,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
apen = QPen(QColor("blue"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,rainbowArcHeight*5,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
apen = QPen(QColor("violet"));
apen.setWidth(rainbowArcHeight);
painter->setPen(apen);
painter->drawArc(0,rainbowArcHeight*6,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
painter->restore();
painter->translate(QPoint(220,-90));
drawSun();
}
Обратите внимание, я использовал методы save() и restore().
Запустим:
Выглядит лучше, а теперь изменим метод отрисовки облаков и изменим их размер:
void MainWindow::drawClouds()
{
painter->translate(QPoint(0,20));
painter->save();
painter->translate(QPoint(20,20));
painter->scale(0.8,0.8);
drawCloud(121);
painter->restore();
painter->save();
painter->translate(QPoint(100,60));
painter->scale(1.2,1.2);
drawCloud(122);
painter->restore();
painter->save();
painter->translate(QPoint(240,100));
painter->scale(0.6,0.6);
drawCloud(123);
painter->restore();
painter->save();
painter->translate(QPoint(225,2));
painter->scale(0.9,0.9);
drawCloud(124);
painter->restore();
}
Для изменения размера мы использовали метод painter->scale
Данный метод принимает числа типа real, таким образом, что, например, обычный размер будет 1,1. 50% меньше будет 0.5,0.5. 150% соответственно 1.5,1.5 и так далее.
Мы получим картинку.
Влияние масштабирования на трансформацию
Обратите внимание - масштабирование влияет на трансляцию, которая будет производиться после вызова метода scale()
Рассмотрим это на примере, нарисуем круг по центру экрана.
painter->translate(QPoint(200,200));
painter->setBrush(Qt::red);
painter->drawEllipse(-25, -25, 50,50);
Получим результат:
Изменим код:
painter->scale(0.5,0.5);
painter->translate(QPoint(200,200));
painter->setBrush(Qt::red);
painter->drawEllipse(-25, -25, 50,50);
Получим результат:
Всегда делайте сначала трансляцию, а уже потом масштабирование, чтобы избежать проблем с расположением при рисовании.
Заключение
Сегодня мы рассмотрели последнюю трансформацию — масштабирование.
Добавили метод для рисования облака. Модернизировали его, таким образом, чтобы он позволял генерировать облака разных размеров.
Добавили зерно (seed) для метода, чтобы гарантировать одинаковый результат при каждом запуске программы.
Обновили метод отрисовки дома, с использованием методов save() и restore() и добавили облака.
Рассмотрели влияние масштабирования на трансляцию в QPainter.
Добавить комментарий