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