Система координат
Система координат управляется классом 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(1, 2, 6, 4) | QLine(2, 7, 6, 1) |
Рисование с алиасингом
При рисовании рендеринг пикселей контролируется параметром рендеринга QPainter::Antialiasing.
Перечисление RenderHint используется для задания флагов для QPainter, которые могут или не могут быть учтены любым заданным движком. Значение QPainter::Antialiasing указывает, что движок должен выполнять сглаживание краёв примитивов, если это возможно, т. е. сглаживать края, используя различные интенсивности цвета.
Но по умолчанию рисовальщик имеет алиасинг, и применяются другие правила: при рендеринге с пером шириной один пиксель пиксели будут отрисовываться справа и снизу от математически определённых точек. Например:
QPainter painter(this); painter.setPen(Qt::darkGreen); 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 anti-aliasing, пиксели будут отрисовываться симметрично по обе стороны от математически определенных точек:
QPainter painter(this);
painter.setRenderHint(
QPainter::Antialiasing);
painter.setPen(Qt::darkGreen);
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) находится в центре виджета, а не в верхнем левом углу. Мы также масштабируем систему на Это даст нам область размером 200x200 с началом координат (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 мы задаём точки, используя логические координаты, которые затем преобразуются в физические координаты устройства рисования.
Преобразование логических координат в физические координаты выполняется матрицей преобразования worldTransform() QPainter (описана в разделе Преобразования) и QPainter's 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/archives/qt-5.11/coordsys.html