Spec-Zone.ru › Qt 5.6

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

Система координат управляется классом 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) находится в центре виджета, а не в верхнем левом углу. Мы также масштабируем систему на 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 (описанной в разделе Преобразования) и областью видимости 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

Spec-Zone.ru

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