Spec-Zone.ru › Qt 5.15

Система координат

Система координат управляется классом 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) находится в центре виджета, а не в верхнем левом углу. Мы также масштабируем систему на side / 100, где side — это либо ширина, либо высота виджета, в зависимости от того, что короче. Мы хотим, чтобы часы были квадратными, даже если устройство нет.

Это даст нам квадратную область размером 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, хранящихся в статической переменной hourHand (определена в начале функции), что соответствует четырём точкам (2, 0), (0, 2), (-2, 0) и (0, -25).

Вызовы 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API