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