Spec-Zone.ru › Qt

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

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

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

Spec-Zone.ru

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