Spec-Zone.ru › Qt 5.13

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

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

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

Spec-Zone.ru

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