Система координат
Система координат управляется классом 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().
| нет | 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's worldTransform() (описано в разделе Преобразования), а также QPainter's viewport() и window(). Viewport представляет физические координаты, определяющие произвольный прямоугольник. «Window» описывает тот же прямоугольник в логических координатах. По умолчанию логическая и физическая системы координат совпадают и эквивалентны прямоугольнику устройства отрисовки.
Используя преобразование window-viewport, вы можете настроить логическую систему координат по своему усмотрению. Этот механизм также можно использовать для того, чтобы код отрисовки не зависел от устройства отрисовки. Например, вы можете задать логические координаты от (-50, -50) до (50, 50) с центром в (0, 0), вызвав функцию QPainter::setWindow():
QPainter painter(this); painter.setWindow(QRect(-50, -50, 100, 100));
Теперь логические координаты (-50, -50) соответствуют физическим координатам устройства отрисовки (0, 0). Независимо от устройства отрисовки, ваш код отрисовки всегда будет работать с заданными логическими координатами.
Установка прямоугольника «window» или viewport выполняет линейное преобразование координат. Обратите внимание, что каждый угол «window» отображается в соответствующий угол viewport, и наоборот. По этой причине обычно рекомендуется, чтобы viewport и «window» имели одинаковый коэффициент пропорциональности, чтобы предотвратить искажения:
int side = qMin(width(), height()); int x = (width() - side / 2); int y = (height() - side / 2); painter.setViewport(x, y, side, side);
Если мы сделаем логическую систему координат квадратной, то мы должны также сделать viewport квадратным, используя функцию QPainter::setViewport(). В примере выше мы делаем его эквивалентным наибольшему квадрату, который помещается в прямоугольник устройства отрисовки. Учитывая размер устройства отрисовки при установке «window» или viewport, можно сохранить независимость кода отрисовки от устройства.
Обратите внимание, что преобразование window-viewport является только линейным преобразованием, т. е. оно не выполняет обрезку. Это означает, что если вы рисуете за пределами текущего «window», ваш рисунок всё равно преобразуется в viewport с использованием того же линейного алгебраического подхода.
Viewport, «window» и матрица преобразования определяют, как логические координаты QPainter отображаются в физические координаты устройства отрисовки. По умолчанию матрица преобразования мира является единичной матрицей, а настройки «window» и viewport эквивалентны настройкам устройства отрисовки, т. е. системы координат мира, «window» и устройства эквивалентны, но, как мы видели, системы могут быть изменены с помощью операций преобразования и преобразования window-viewport. На иллюстрации выше описан этот процесс.
См. также Пример аналоговых часов.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/coordsys.html