Система координат
Система координат контролируется классом QPainter. Вместе с классами QPaintDevice и QPaintEngine, класс QPainter образует основу системы рисования Qt, Arthur. QPainter используется для выполнения операций рисования, QPaintDevice — это абстракция двумерного пространства, на котором можно рисовать с помощью QPainter, а QPaintEngine предоставляет интерфейс, который используется художником для рисования на различных типах устройств.
Класс QPaintDevice — это базовый класс объектов, которые могут быть нарисованы: его возможности рисования унаследованы классами QWidget, QImage, QPixmap, QPicture и QOpenGLPaintDevice. Система координат устройства по умолчанию имеет начало координат в верхнем левом углу. Значения x увеличиваются вправо, а значения y увеличиваются вниз. Единица измерения по умолчанию составляет один пиксель для устройств на основе пикселей и один пункт (1/72 дюйма) для принтеров.
Преобразование логических координат QPainter в физические координаты QPaintDevice обрабатывается матрицей преобразования, областью просмотра и «окном» QPainter. Логическая и физическая системы координат совпадают по умолчанию. QPainter также поддерживает преобразования координат (например, поворот и масштабирование).
Рендеринг
Логическое представление
Размер (ширина и высота) графического примитива всегда соответствует его математической модели, игнорируя ширину пера, которым он отрисовывается:
| QRect(QPoint(1, 2), QPoint(7, 6)) | QLine(QPoint(2, 7), QPoint(6, 1)) |
| QLine(2, 7, 6, 1) | |
| QRect(QPoint(1, 2), QSize(6, 4)) | |
| QRect(1, 2, 6, 4) |
Рисование с алиасингом
При рисовании отрисовка пикселей контролируется флагом рендеринга QPainter::Antialiasing.
Перечисление RenderHint используется для указания флагов для QPainter, которые могут или не могут быть учтены каким-либо движком. Значение QPainter::Antialiasing указывает, что движок должен выполнять сглаживание краев примитивов, если это возможно, то есть сглаживать края, используя различные интенсивности цвета.
Но по умолчанию художник алиасирован, и применяются другие правила: при рисовании с пером шириной один пиксель пиксели будут отрисованы вправо и ниже математически определенных точек. Например:
QPainter painter(this); painter.setPen(Qt::darkGreen); // Using the (x y w h) overload painter.drawRect(1, 2, 6, 4); |
QPainter painter(this); painter.setPen(Qt::darkGreen); painter.drawLine(2, 7, 6, 1); |
При рисовании пером с четным числом пикселей пиксели будут отрисовываться симметрично вокруг математически определенных точек, а при рисовании пером с нечетным числом пикселей лишний пиксель будет отрисовываться вправо и ниже математической точки, как и в случае с одним пикселем. См. диаграммы QRectF ниже для конкретных примеров.
| QRectF | ||
|---|---|---|
| Логическое представление | Перо шириной один пиксель | |
| Перо шириной два пикселя | Перо шириной три пикселя | |
Обратите внимание, что по историческим причинам возвращаемое значение функций QRect::right() и QRect::bottom() отличается от истинной точки в правом нижнем углу прямоугольника.
QRect's right() возвращает left() + width() - 1, а bottom() возвращает top() + height() - 1. Зеленая точка в правом нижнем углу диаграмм показывает возвращаемые координаты этих функций.
Мы рекомендуем использовать QRectF вместо этого: класс QRectF определяет прямоугольник в плоскости, используя координаты с плавающей точкой для точности (QRect использует целочисленные координаты), а функции QRectF::right() и QRectF::bottom() возвращают истинную точку в правом нижнем углу.
В качестве альтернативы, используя QRect, используйте x() + width() и y() + height(), чтобы найти точку в правом нижнем углу, и избегайте использования функций right() и bottom().
Рисование со сглаживанием
Если вы установите флага QPainter's сглаживания рендеринга, пиксели будут отрисовываться симметрично по обе стороны от математически определенных точек:
QPainter painter(this);
painter.setRenderHint(
QPainter::Antialiasing);
painter.setPen(Qt::darkGreen);
// Using the (x y w h) overload
painter.drawRect(1, 2, 6, 4); |
QPainter painter(this);
painter.setRenderHint(
QPainter::Antialiasing);
painter.setPen(Qt::darkGreen);
painter.drawLine(2, 7, 6, 1); |
Преобразования
По умолчанию QPainter работает в собственной системе координат связанного устройства, но также полностью поддерживает аффинные преобразования координат.
Вы можете масштабировать систему координат на заданный сдвиг, используя функцию QPainter::scale(), повернуть ее по часовой стрелке с помощью функции QPainter::rotate() и переместить ее (то есть добавить заданный сдвиг к точкам) с помощью функции QPainter::translate().
| nop | rotate() | scale() | translate() |
Вы также можете исказить систему координат вокруг начала координат, используя функцию QPainter::shear(). Все операции преобразования действуют на матрицу преобразования QPainter, которую можно получить, используя функцию QPainter::worldTransform(). Матрица преобразует точку на плоскости в другую точку.
Если вам нужно применять одни и те же преобразования многократно, вы также можете использовать объекты QTransform и функции QPainter::worldTransform() и QPainter::setWorldTransform(). Вы можете в любое время сохранить матрицу преобразования QPainter, вызвав функцию QPainter::save(), которая сохраняет матрицу в внутреннем стеке. Функция QPainter::restore() возвращает ее обратно.
Часто требуется матрица преобразования при повторном использовании одного и того же кода рисования на различных устройствах рисования. Без преобразований результаты тесно связаны с разрешением устройства рисования. У принтеров высокое разрешение, например, 600 точек на дюйм, в то время как экраны часто имеют разрешение от 72 до 100 точек на дюйм.
| Пример аналоговых часов | |
|---|---|
| Пример аналоговых часов демонстрирует, как нарисовать содержимое пользовательского виджета с помощью матрицы преобразования QPainter. Мы рекомендуем скомпилировать и запустить этот пример, прежде чем читать дальше. В частности, попробуйте изменить размер окна. |
|
void AnalogClockWindow::render(QPainter *p)
{
static const QPoint hourHand[3] = {
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -40)
};
static const QPoint minuteHand[3] = {
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -70)
};
QColor hourColor(127, 0, 127);
QColor minuteColor(0, 127, 127, 191);
p->setRenderHint(QPainter::Antialiasing);
p->translate(width() / 2, height() / 2);
int side = qMin(width(), height());
p->scale(side / 200.0, side / 200.0); Мы перемещаем систему координат так, что точка (0, 0) находится в центре виджета, а не в верхнем левом углу. Мы также масштабируем систему на Это даст нам квадрат размером 200 х 200 с началом координат (0, 0) в центре, на котором мы можем рисовать. То, что мы нарисуем, будет отображаться в максимально возможном квадрате, который поместится в виджет. См. также раздел «Преобразование окна-области просмотра». QTime time = QTime::currentTime();
p->save();
p->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
p->drawConvexPolygon(hourHand, 3);
p->restore(); Мы рисуем часовую стрелку, поворачивая систему координат и вызывая QPainter::drawConvexPolygon(). Благодаря повороту, она рисуется в нужном направлении. Многоугольник задается как массив чередующихся значений x, y, хранящихся в статической переменной Вызовы QPainter::save() и QPainter::restore() вокруг кода гарантируют, что последующий код не будет нарушен используемыми нами преобразованиями. p->save();
p->rotate(6.0 * (time.minute() + time.second() / 60.0));
p->drawConvexPolygon(minuteHand, 3);
p->restore(); Мы делаем то же самое для минутной стрелки, которая определяется четырьмя точками (1, 0), (0, 1), (-1, 0) и (0, -40). Эти координаты определяют стрелку, которая тоньше и длиннее минутной стрелки. p->setPen(minuteColor);
for (int j = 0; j < 60; ++j) {
if ((j % 5) != 0)
p->drawLine(92, 0, 96, 0);
p->rotate(6.0);
} Наконец, мы рисуем циферблат, который состоит из двенадцати коротких линий через 30-градусные интервалы. В конце этого цикла художник поворачивается так, что это не очень полезно, но мы закончили рисование, поэтому это не имеет значения. | |
Дополнительную информацию о матрице преобразования можно найти в документации по QTransform.
Преобразование окна-области просмотра
При рисовании с помощью QPainter мы указываем точки, используя логические координаты, которые затем преобразуются в физические координаты устройства рисования.
Сопоставление логических координат с физическими координатами осуществляется с помощью преобразования мира QPainter worldTransform() (описано в разделе Преобразования), а также QPainter viewport() и window(). Вид представляет физические координаты, определяя произвольную прямоугольную область. «Окно» описывает ту же прямоугольную область в логических координатах. По умолчанию, логическая и физическая системы координат совпадают и эквивалентны прямоугольнику устройства отрисовки.
Используя преобразование «окно-вид», вы можете настроить логическую систему координат под свои нужды. Этот механизм также может быть использован для создания кода отрисовки, независимого от устройства отрисовки. Например, вы можете установить логические координаты от (-50, -50) до (50, 50) с центром в (0, 0), вызвав функцию QPainter::setWindow():
QPainter painter(this); painter.setWindow(QRect(-50, -50, 100, 100));
Теперь логические координаты (-50,-50) соответствуют физическим координатам устройства отрисовки (0, 0). Независимо от устройства отрисовки, ваш код отрисовки всегда будет работать с заданными логическими координатами.
Устанавливая прямоугольник «окно» или вид, вы выполняете линейное преобразование координат. Обратите внимание, что каждый угол «окна» отображается на соответствующий угол вида, и наоборот. По этой причине обычно рекомендуется сохранять одинаковый коэффициент пропорциональности для вида и «окна», чтобы избежать искажений:
int side = qMin(width(), height()) int x = (width() - side / 2); int y = (height() - side / 2); painter.setViewport(x, y, side, side);
Если мы сделаем логическую систему координат квадратной, мы должны сделать вид также квадратным, используя функцию QPainter::setViewport(). В приведенном выше примере мы делаем его эквивалентным самому большому квадрату, который поместится в прямоугольник устройства отрисовки. Учитывая размер устройства отрисовки при установке «окна» или вида, можно сохранить код отрисовки независимым от устройства отрисовки.
Обратите внимание, что преобразование «окно-вид» является только линейным преобразованием, то есть оно не выполняет обрезку. Это означает, что если вы рисуете за пределами текущего «окна», ваша отрисовка всё равно преобразуется в вид с использованием того же линейного алгебраического подхода.
Вид, «окно» и матрица преобразования определяют, как логические координаты QPainter отображаются в физические координаты устройства отрисовки. По умолчанию матрица преобразования мира является единичной матрицей, а настройки «окна» и вида эквивалентны настройкам устройства отрисовки, то есть системы координат мира, «окна» и устройства эквивалентны, но, как мы видели, эти системы могут быть изменены с помощью операций преобразования и преобразования «окно-вид». Иллюстрация выше описывает этот процесс.
См. также Пример окна аналоговых часов.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.13/coordsys.html