Система координат
Система координат управляется классом 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 сглаживание, пиксели будут отрисовываться симметрично по обе стороны от математически определённых точек:
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 worldTransform() (описано в разделе Преобразования) и функциями QPainter 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.2/coordsys.html