Spec-Zone.ru › Qt 5.11

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

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

Это даст нам область размером 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, хранящихся в статической переменной 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.

Преобразование «окно-область просмотра»

При рисовании с помощью 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

Spec-Zone.ru

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