이 페이지에서

좌표계

좌표계는 ` QPainter ` 클래스에 의해 제어됩니다. ` QPaintDevice ` 및 ` QPaintEngine ` 클래스와 함께 ` QPainter `는 Qt의 페인팅 시스템인 Arthur의 기반을 형성합니다. ` QPainter `는 그리기 작업을 수행하는 데 사용되며, ` QPaintDevice `는 ` QPainter`를 사용하여 페인팅할 수 있는 2차원 공간의 추상화이며, ` QPaintEngine `는 페인터가 다양한 유형의 장치에 그리기 위해 사용하는 인터페이스를 제공합니다.

QPaintDevice 클래스는 그리기 가능한 객체의 기본 클래스입니다. 이 클래스의 그리기 기능은 QWidget, QImage, QPixmap, QPicture 및 QOpenGLPaintDevice 클래스에 상속됩니다. 페인트 장치의 기본 좌표계는 왼쪽 상단 모서리를 원점으로 합니다. x 값은 오른쪽으로 갈수록 증가하고, y 값은 아래로 갈수록 증가합니다. 기본 단위는 픽셀 기반 장치에서는 1픽셀이며, 프린터에서는 1포인트(1인치의 1/72)입니다.

논리적 QPainter 좌표와 물리적 QPaintDevice 좌표 간의 매핑은 QPainter 의 변환 행렬, 뷰포트 및 “창”에 의해 처리됩니다. 기본적으로 논리적 좌표계와 물리적 좌표계는 일치합니다. QPainter 는 좌표 변환(예: 회전 및 크기 조정)도 지원합니다.

렌더링

논리적 표현

그래픽스 프리미티브의 크기(너비와 높이)는 렌더링에 사용되는 펜의 너비를 무시하고 항상 해당 수학적 모델에 부합합니다:

(1,2)에서 (7,6)까지 좌표 격자 위에 있는 직사각형좌표 평면상에서 (2,7)에서 (6,1)까지 이어지는 직선
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 값은 엔진이 가능한 경우 프리미티브의 가장자리에 앤티앨리어싱을 적용해야 함을 나타냅니다. 즉, 다양한 색상 강도를 사용하여 가장자리를 부드럽게 처리해야 함을 의미합니다.

그러나 기본적으로 페인터는 앨리어싱이 발생하며 다른 규칙이 적용됩니다. 1픽셀 너비의 펜으로 렌더링할 경우, 픽셀은 수학적으로 정의된 점의 오른쪽과 아래쪽에 렌더링됩니다. 예를 들어:

오른쪽과 아래쪽에 픽셀이 렌더링된 별칭이 붙은 사각형오른쪽과 아래쪽에 픽셀이 렌더링된 앨리어싱 현상이 발생한 선
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);

짝수 픽셀 너비의 펜으로 렌더링할 경우, 픽셀은 수학적으로 정의된 점을 중심으로 대칭적으로 렌더링되는 반면, 홀수 픽셀 너비의 펜으로 렌더링할 경우, 여분의 픽셀은 1픽셀 너비 펜의 경우와 마찬가지로 수학적으로 정의된 점의 오른쪽과 아래쪽에 렌더링됩니다. 구체적인 예시는 아래의 QRectF 다이어그램을 참조하십시오.

QRectF
width()와 height() 치수가 표시된 직사각형모서리 좌표를 나타내는 1픽셀 두께의 선으로 그려진 직사각형
논리적 표현폭 1픽셀 펜
두 픽셀 두께의 펜으로 그려진 사각형과 모서리 좌표모서리 좌표를 표시한 3픽셀 두께의 사각형
2픽셀 폭 펜3픽셀 폭 펜

역사적인 이유로 인해 QRect::right() 및 QRect::bottom() 함수의 반환 값은 사각형의 실제 오른쪽 하단 모서리와 차이가 있다는 점에 유의하십시오.

QRect right() 함수는 () + () - 1을 반환하고, () 함수는 () + () - 1을 반환합니다. 다이어그램의 오른쪽 하단 녹색 점은 이러한 함수의 반환 좌표를 나타냅니다. left width bottom top height

대신 QRectF 를 사용하는 것을 권장합니다. QRectF 클래스는 정확도를 위해 부동 소수점 좌표를 사용하여 평면상의 사각형을 정의하며(QRect 는 정수 좌표를 사용함), QRectF::right() 및 QRectF::bottom() 함수는 실제 오른쪽 하단 모서리를 반환합니다.

또는 QRect 를 사용하여 x() + width()과 y() + height()을 적용해 오른쪽 아래 모서리를 구하고, right() 및 bottom() 함수는 사용하지 마십시오.

앤티앨리어싱 적용

QPainter 의 anti-aliasing 렌더링 힌트를 설정하면, 픽셀이 수학적으로 정의된 점의 양쪽에서 대칭적으로 렌더링됩니다:

대칭 픽셀 렌더링이 적용된 앤티앨리어싱 처리된 직사각형대칭 픽셀 렌더링이 적용된 앤티앨리어싱 처리된 선
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() 함수를 사용하여 평행 이동(즉, 점에 주어진 오프셋을 더하는 것)할 수 있습니다.

또한 QPainter::shear() 함수를 사용하여 원점을 중심으로 좌표계를 비틀 수 있습니다. 모든 변환 연산은 QPainter 의 변환 행렬을 기반으로 수행되며, 이 행렬은 QPainter::worldTransform() 함수를 사용하여 가져올 수 있습니다. 행렬은 평면상의 한 점을 다른 점으로 변환합니다.

동일한 변환을 반복적으로 수행해야 하는 경우, QTransform 객체와 QPainter::worldTransform() 및 QPainter::setWorldTransform() 함수를 사용할 수도 있습니다. QPainter::save() 함수를 호출하여 QPainter 의 변환 행렬을 내부 스택에 저장할 수 있으며, 이 행렬은 언제든지 불러올 수 있습니다. QPainter::restore() 함수는 이를 다시 꺼냅니다.

변환 행렬이 자주 필요한 경우 중 하나는 다양한 출력 장치에서 동일한 그리기 코드를 재사용할 때입니다. 변환을 적용하지 않으면 결과물이 출력 장치의 해상도에 엄격하게 종속됩니다. 프린터는 인치당 600도트와 같이 높은 해상도를 갖는 반면, 화면은 대개 인치당 72~100도트 정도입니다.

아날로그 시계 예제
시침과 분침을 표시하는 아날로그 시계 위젯아날로그 시계 예제는 QPainter 의 변환 행렬을 사용하여 사용자 정의 위젯의 내용을 그리는 방법을 보여줍니다.

더 읽기 전에 이 예제를 컴파일하여 실행해 보시는 것을 권장합니다. 특히, 창 크기를 여러 가지로 조정해 보십시오.

void AnalogClock::paintEvent(QPaintEvent *)
{
    static const QPoint hourHand[4] = {
        QPoint(5, 14),
        QPoint(-5, 14),
        QPoint(-4, -71),
        QPoint(4, -71)
    };
    static const QPoint minuteHand[4] = {
        QPoint(4, 14),
        QPoint(-4, 14),
        QPoint(-3, -89),
        QPoint(3, -89)
    };

    static const QPoint secondsHand[4] = {
       QPoint(1, 14),
       QPoint(-1, 14),
       QPoint(-1, -89),
       QPoint(1, -89)
    };

    const QColor hourColor(palette().color(QPalette::Text));
    const QColor minuteColor(palette().color(QPalette::Text));
    const QColor secondsColor(palette().color(QPalette::Accent));

    int side = qMin(width(), height());

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.translate(width() / 2, height() / 2);
    painter.scale(side / 200.0, side / 200.0);

좌표계를 변환하여 점 (0, 0)이 왼쪽 상단 모서리가 아닌 위젯의 중앙에 오도록 합니다. 또한 side / 200을 곱하여 좌표계를 확대/축소합니다. 여기서 side 는 위젯의 너비나 높이 중 더 짧은 쪽입니다. 기기가 정사각형이 아니더라도 시계는 정사각형으로 표시되도록 하기 위함입니다.

이렇게 하면 원점 (0, 0)이 중앙에 위치한 200 x 200 크기의 정사각형 영역이 생성되며, 이 영역에 그림을 그릴 수 있습니다. 우리가 그린 내용은 위젯에 들어갈 수 있는 가장 큰 정사각형 영역 내에 표시됩니다.

'창-뷰포트 변환' 섹션도 참조하십시오.

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
        painter.drawConvexPolygon(hourHand, 4);
    }

좌표계를 회전시키고 ` QPainter::drawConvexPolygon()`를 호출하여 시계의 시침을 그립니다. 회전 덕분에 시침이 올바른 방향을 가리키도록 그려집니다.

다각형은 x, y 값이 번갈아 가며 저장된 배열로 지정되며, 이는 함수 시작 부분에 정의된 hourHand 정적 변수에 저장되어 세 점 (7, 8), (-7, 8), (0, -40)에 해당합니다.

코드를 둘러싸고 있는 QPainter::save() 및 QPainter::restore() 호출은 뒤따르는 코드가 우리가 사용한 변환에 의해 영향을 받지 않도록 보장합니다.

    painter.setBrush(minuteColor);

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(6.0 * time.minute());
        painter.drawConvexPolygon(minuteHand, 4);
    }

그 후, 30도 간격으로 12개의 짧은 선으로 구성된 시계 면의 시간 표시를 그립니다. 이 루프가 완료되면 페인터는 한 바퀴 회전하여 원래 상태로 돌아오므로, 상태를 저장하고 복원할 필요가 없습니다.

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(6.0 * time.second());
        painter.drawConvexPolygon(secondsHand, 4);
        painter.drawEllipse(-3, -3, 6, 6);
        painter.drawEllipse(-5, -68, 10, 10);
    }

시계의 분침에 대해서도 동일한 방식을 적용합니다. 분침은 (7, 8), (-7, 8), (0, -70)이라는 세 점으로 정의됩니다. 이 좌표들은 분침보다 더 가늘고 긴 바늘을 나타냅니다.

    for (int j = 0; j < 60; ++j) {
        painter.drawLine(92, 0, 96, 0);
        painter.rotate(6.0);
    }

마지막으로, 6도 간격으로 60개의 짧은 선으로 이루어진 시계판의 분 눈금을 그립니다. 시 표시를 가리지 않기 위해 5개 중 1개씩 분 표시를 건너뜁니다. 이 과정이 끝나면 페인터 객체가 별로 유용하지 않은 방향으로 회전되지만, 그리기 작업은 모두 완료되었으므로 이는 문제가 되지 않습니다.

변환 행렬에 대한 자세한 내용은 QTransform 문서를 참조하십시오.

창-뷰포트 변환

QPainter 로 그릴 때, 우리는 논리 좌표를 사용하여 점을 지정하고, 이 좌표는 페인트 장치의 물리 좌표로 변환됩니다.

논리적 좌표와 물리적 좌표 간의 매핑은 QPainter 의 세계 변환 worldTransform() ( 변환 섹션에서 설명)과 QPainter 의 viewport() 및 window()에 의해 처리됩니다. 뷰포트는 임의의 직사각형을 지정하는 물리적 좌표를 나타냅니다. "window"는 논리적 좌표계에서 동일한 직사각형을 나타냅니다. 기본적으로 논리적 좌표계와 물리적 좌표계는 일치하며, 페인트 장치의 직사각형과 동일합니다.

윈도우-뷰포트 변환을 사용하면 논리적 좌표계를 사용자의 선호에 맞게 조정할 수 있습니다. 이 메커니즘은 그리기 코드를 페인트 장치와 독립적으로 만드는 데에도 사용할 수 있습니다. 예를 들어, ` QPainter::setWindow()` 함수를 호출하여 논리적 좌표가 (-50, -50)부터 (50, 50)까지 확장되고 (0, 0)을 중심으로 하도록 설정할 수 있습니다:

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)” 및 변환 행렬을 사용하여 좌표가 매핑되는 방식을 보여주는 그림

뷰포트, “창” 및 변환 행렬은 논리적 QPainter 좌표가 페인트 장치의 물리적 좌표로 어떻게 매핑되는지를 결정합니다. 기본적으로 월드 변환 행렬은 단위 행렬이며, “창” 및 뷰포트 설정은 페인트 장치의 설정과 동일합니다. 즉, 월드, “창” 및 장치 좌표계는 서로 동일하지만, 앞서 살펴본 바와 같이 변환 연산과 창-뷰포트 변환을 사용하여 이러한 좌표계를 조작할 수 있습니다. 위의 그림은 이 과정을 설명하고 있습니다.

‘아날로그 시계’항목도 참조하십시오 .

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.