Система координат
Система координат управляется классом QPainter. Вместе с классами QPaintDevice и QPaintEngine класс QPainter образует основу системы рисования Qt, Arthur. QPainter используется для выполнения операций рисования, QPaintDevice — это абстракция двумерного пространства, на котором можно рисовать с помощью QPainter, а QPaintEngine предоставляет интерфейс, который использует рисовальщик для рисования на разных типах устройств.
Класс QPaintDevice является базовым классом объектов, на которых можно рисовать: его возможности рисования унаследованы классами QWidget, QImage, QPixmap, QPicture и QOpenGLPaintDevice. Система координат устройства по умолчанию имеет начало в верхнем левом углу. Значения x увеличиваются вправо, а значения y — вниз. Единицей измерения по умолчанию является один пиксель на устройствах, основанных на пикселях, и один пункт (1/72 дюйма) на принтерах.
Преобразование логических координат QPainter в физические координаты QPaintDevice выполняется матрицей преобразования QPainter, видимой областью (viewport) и "окном" (window). Логическая и физическая системы координат совпадают по умолчанию. 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 указывает, что движок должен сглаживать края примитивов, если это возможно, то есть сглаживать края, используя разные интенсивности цвета.
Но по умолчанию рисовальщик с alia싱 и применяются другие правила: при отрисовке пером шириной один пиксель пиксели будут отрисованы справа и снизу от математически определенных точек. Например:
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 вместо QRect: класс QRectF определяет прямоугольник в плоскости с использованием координат с плавающей точкой для большей точности (QRect использует целочисленные координаты), а функции QRectF::right() и QRectF::bottom() возвращают истинную правую нижнюю точку.
В качестве альтернативы, при использовании QRect, используйте x() + width() и y() + height() для нахождения правой нижней точки, чтобы избежать использования функций right() и bottom().
Отрисовка с антиалиасингом
Если вы установили флаг антиалиасинга QPainter anti-aliasing, пиксели будут отрисованы симметрично по обе стороны от математически определенных точек:
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 x 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) описывает тот же прямоугольник в логических координатах. По умолчанию логические и физические системы координат совпадают и эквивалентны прямоугольнику устройства рисования.
Используя преобразование «окно-окно обзора», вы можете сделать логическую систему координат соответствующей вашим предпочтениям. Этот механизм также можно использовать для того, чтобы код рисования был независим от устройства рисования. Например, вы можете сделать логические координаты, простирающиеся от (-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-6.0/coordsys.html