변환 예제
'변환 예제'는 변환이 QPainter 에서 그래픽 프리미티브를 렌더링하는 방식에 어떤 영향을 미치는지 보여줍니다.

이 애플리케이션을 통해 사용자는 QPainter 의 좌표계에서 평행 이동, 회전 및 크기를 변경하여 도형의 렌더링을 조작할 수 있습니다.
이 예제는 두 개의 클래스와 하나의 전역 열거형으로 구성됩니다:
RenderArea클래스는 주어진 도형의 렌더링을 제어합니다.Window클래스는 애플리케이션의 메인 창입니다.Operation열거형은 애플리케이션에서 사용할 수 있는 다양한 변환 연산을 정의합니다.
먼저 ` Operation ` 열거형을 간략히 살펴본 다음, ` RenderArea ` 클래스를 검토하여 도형이 어떻게 렌더링되는지 확인하겠습니다. 마지막으로, ` Window ` 클래스에 구현된 `Transformations` 애플리케이션의 기능들을 살펴보겠습니다.
변환 연산
일반적으로 ` QPainter `는 관련 장치의 자체 좌표계를 기반으로 작동하지만, 좌표 변환에 대한 지원도 잘 되어 있습니다.
페인트 장치의 기본 좌표계는 왼쪽 상단 모서리를 원점으로 합니다. x 값은 오른쪽으로 갈수록 증가하고, y 값은 아래로 갈수록 증가합니다. QPainter::scale() 함수를 사용하여 좌표계를 지정된 오프셋만큼 확대/축소할 수 있고, QPainter::rotate() 함수를 사용하여 시계 방향으로 회전시킬 수 있으며, QPainter::translate() 함수를 사용하여 평행 이동(즉, 점에 지정된 오프셋을 더하는 것)할 수 있습니다. 또한 QPainter::shear() 함수를 사용하여 좌표계를 원점을 중심으로 비틀어(전단이라고 함) 변형할 수도 있습니다.
모든 변환 연산은 QPainter::worldTransform() 함수를 사용하여 가져올 수 있는 QPainter 의 변환 행렬을 기반으로 수행됩니다. 행렬은 평면상의 한 점을 다른 점으로 변환합니다. 변환 행렬에 대한 자세한 내용은 ‘좌표계(Coordinate System )’ 및 ‘좌표 변환( QTransform )’ 문서를 참조하십시오.
enum Operation { NoTransformation, Translate, Rotate, Scale };전역 Operation 열거형은 renderarea.h 파일에 선언되어 있으며, Transformations 애플리케이션에서 사용할 수 있는 다양한 변환 연산을 설명합니다.
RenderArea 클래스 정의
RenderArea 클래스는 QWidget 을 상속받으며, 주어진 도형의 렌더링을 제어합니다.
class RenderArea : public QWidget
{
Q_OBJECT
public:
RenderArea(QWidget *parent = nullptr);
void setOperations(const QList<Operation> &operations);
void setShape(const QPainterPath &shape);
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;RenderArea 위젯의 도형을 지정하고 도형이 렌더링되는 좌표계를 변환할 수 있도록 setOperations() 및 setShape() 라는 두 개의 공용 함수를 선언합니다.
QWidget 의 minimumSizeHint() 및 sizeHint() 함수를 재구현하여 RenderArea 위젯이 애플리케이션 내에서 적절한 크기를 갖도록 하고, QWidget::paintEvent() 이벤트 핸들러를 재구현하여 사용자가 선택한 변환을 적용하여 렌더링 영역의 도형을 그립니다.
private:
void drawCoordinates(QPainter &painter);
void drawOutline(QPainter &painter);
void drawShape(QPainter &painter);
void transformPainter(QPainter &painter);
QList<Operation> operations;
QPainterPath shape;
QRect xBoundingRect;
QRect yBoundingRect;
};또한 도형, 좌표계의 윤곽선 및 좌표를 그리기 위해, 그리고 선택된 변환에 따라 페인터를 변환하기 위해 몇 가지 편의 함수를 선언합니다.
또한, ` RenderArea ` 위젯은 현재 적용된 변환 연산 목록, 도형에 대한 참조, 그리고 좌표를 렌더링할 때 사용할 몇 가지 편의 변수를 유지합니다.
RenderArea 클래스 구현
RenderArea 위젯은 ` QWidget::paintEvent()` 이벤트 핸들러를 재구현하여, 좌표계의 변환을 포함하여 주어진 도형의 렌더링을 제어합니다. 하지만 먼저 생성자와 ` RenderArea ` 위젯에 대한 액세스를 제공하는 함수들을 간단히 살펴보겠습니다:
RenderArea::RenderArea(QWidget *parent)
: QWidget(parent)
{
QFont newFont = font();
newFont.setPixelSize(12);
setFont(newFont);
QFontMetrics fontMetrics(newFont);
xBoundingRect = fontMetrics.boundingRect(tr("x"));
yBoundingRect = fontMetrics.boundingRect(tr("y"));
}생성자에서는 parent 매개변수를 기본 클래스로 전달하고, 좌표를 렌더링하는 데 사용할 글꼴을 사용자 정의합니다. QWidget::font() 함수는 위젯에 현재 설정된 글꼴을 반환합니다. 특별한 글꼴이 설정되지 않은 상태이거나 QWidget::setFont()이 호출된 후에는, 이 값은 위젯 클래스 전용 글꼴, 부모의 글꼴 또는(이 위젯이 최상위 위젯인 경우) 애플리케이션의 기본 글꼴 중 하나가 됩니다.
글꼴 크기가 12포인트인지 확인한 후, ` QFontMetrics ` 클래스를 사용하여 좌표 문자인 'x'와 'y'를 둘러싸는 사각형을 추출합니다.
QFontMetrics QFontMetrics::boundingRect() 함수는 폰트, 그 문자, 그리고 해당 폰트로 렌더링된 문자열의 개별 메트릭에 접근할 수 있는 함수를 제공합니다. () 함수는 기준선의 가장 왼쪽 지점을 기준으로 주어진 문자의 경계 사각형을 반환합니다.
void RenderArea::setOperations(const QList<Operation> &operations)
{
this->operations = operations;
update();
}
void RenderArea::setShape(const QPainterPath &shape)
{
this->shape = shape;
update();
}setShape() 및 setOperations() 함수에서는 새로운 값을 저장한 후 QWidget::update() 슬롯을 호출하여 RenderArea 위젯을 업데이트하며, 이 호출은 Qt가 메인 이벤트 루프로 돌아왔을 때 처리될 페인트 이벤트를 예약합니다.
QSize RenderArea::minimumSizeHint() const
{
return QSize(182, 182);
}
QSize RenderArea::sizeHint() const
{
return QSize(232, 232);
}QWidget 의 minimumSizeHint() 및 sizeHint() 함수를 재구현하여, 애플리케이션 내에서 RenderArea 위젯에 적절한 크기를 부여합니다. 이 함수들의 기본 구현은 해당 위젯에 레이아웃이 없는 경우 유효하지 않은 크기를 반환하고, 그렇지 않은 경우에는 레이아웃의 최소 크기 또는 선호 크기를 각각 반환합니다.
void RenderArea::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.fillRect(event->rect(), QBrush(Qt::white));
painter.translate(66, 66);paintEvent() 이벤트 핸들러는 RenderArea 위젯의 페인트 이벤트를 수신합니다. 페인트 이벤트는 위젯의 전체 또는 일부를 다시 그리도록 요청하는 것입니다. 이는 QWidget::repaint() 또는 QWidget::update()의 결과로 발생할 수도 있고, 위젯이 가려졌다가 다시 드러난 경우, 혹은 그 밖의 여러 가지 이유로 발생할 수 있습니다.
먼저 RenderArea 위젯에 대한 QPainter 를 생성합니다. QPainter::Antialiasing 렌더링 힌트는 엔진이 가능한 경우 프리미티브의 가장자리에 앤티앨리어싱을 적용해야 함을 나타냅니다. 그런 다음 QPainter::fillRect() 함수를 사용하여 다시 그려야 할 영역을 지웁니다.
또한 원래 도형이 적절한 여백을 두고 렌더링되도록 좌표계를 일정한 오프셋만큼 변환합니다.
{
QPainterStateGuard guard(&painter);
transformPainter(painter);
drawShape(painter);
}도형을 렌더링하기 전에, QPainterStateGuard 를 인스턴스화하여 범위 내의 현재 좌표계를 포함한 현재 페인터 상태(즉, 상태를 스택에 밀어 넣음)를 저장합니다. 페인터 상태를 저장하는 이유는, 이후에 호출될 transformPainter() 함수가 현재 선택된 변환 연산에 따라 좌표계를 변환하게 되며, 윤곽선을 그리기 위해 원래 상태로 되돌릴 방법이 필요하기 때문입니다.
좌표계를 변환한 후, ` RenderArea`의 모양을 그리고, ` QPainter::restore()` 함수를 사용하여 페인터 상태를 복원합니다(즉, 저장된 상태를 스택에서 팝합니다).
drawOutline(painter);그런 다음 정사각형 윤곽선을 그립니다.
transformPainter(painter);
drawCoordinates(painter);
}좌표가 도형이 렌더링되는 좌표계와 일치하도록 하려면, transformPainter() 함수를 한 번 더 호출해야 합니다.
공유되는 픽셀과 관련하여 그리기 작업의 순서는 매우 중요합니다. 도형을 렌더링하기 위해 좌표계가 이미 변환된 상태에서 좌표를 렌더링하지 않고, 대신 렌더링을 마지막까지 미루는 이유는 좌표가 도형과 그 윤곽선 위에 표시되도록 하기 위해서입니다.
좌표 그리기가 마지막 그리기 작업이므로, 이번에는 ` QPainter ` 상태를 저장할 필요가 없습니다.
void RenderArea::drawCoordinates(QPainter &painter)
{
painter.setPen(Qt::red);
painter.drawLine(0, 0, 50, 0);
painter.drawLine(48, -2, 50, 0);
painter.drawLine(48, 2, 50, 0);
painter.drawText(60 - xBoundingRect.width() / 2,
0 + xBoundingRect.height() / 2, tr("x"));
painter.drawLine(0, 0, 0, 50);
painter.drawLine(-2, 48, 0, 50);
painter.drawLine(2, 48, 0, 50);
painter.drawText(0 - yBoundingRect.width() / 2,
60 + yBoundingRect.height() / 2, tr("y"));
}
void RenderArea::drawOutline(QPainter &painter)
{
painter.setPen(Qt::darkGreen);
painter.setPen(Qt::DashLine);
painter.setBrush(Qt::NoBrush);
painter.drawRect(0, 0, 100, 100);
}
void RenderArea::drawShape(QPainter &painter)
{
painter.fillPath(shape, Qt::blue);
}drawCoordinates(), drawOutline() 및 drawShape() 는 paintEvent() 이벤트 핸들러에서 호출되는 편의 함수입니다. QPainter 의 기본 그리기 작업 및 기본 그래픽 프리미티브 표시 방법에 대한 자세한 내용은 ‘기본 그리기 ( Basic Drawing )’ 예제를 참조하십시오.
void RenderArea::transformPainter(QPainter &painter)
{
for (int i = 0; i < operations.size(); ++i) {
switch (operations[i]) {
case Translate:
painter.translate(50, 50);
break;
case Scale:
painter.scale(0.75, 0.75);
break;
case Rotate:
painter.rotate(60);
break;
case NoTransformation:
default:
;
}
}
}transformPainter() 편의 함수도 paintEvent() 이벤트 핸들러에서 호출되며, 사용자가 선택한 변환 설정에 따라 지정된 QPainter 의 좌표계를 변환합니다.
Window 클래스 정의
Window 클래스는 Transformations 애플리케이션의 메인 창입니다.
이 애플리케이션은 네 개의 RenderArea 위젯을 표시합니다. 가장 왼쪽에 있는 위젯은 QPainter 의 기본 좌표계에서 도형을 렌더링하고, 나머지 위젯들은 왼쪽에 있는 RenderArea 위젯에 적용된 모든 변형에 더해 사용자가 선택한 변형을 적용하여 도형을 렌더링합니다.
class Window : public QWidget
{
Q_OBJECT
public:
Window();
public slots:
void operationChanged();
void shapeSelected(int index);응용 프로그램이 사용자 상호 작용에 반응하여 사용자의 변환 선택에 따라 표시된 ` RenderArea ` 위젯을 업데이트할 수 있도록 두 개의 공용 슬롯을 선언합니다.
operationChanged() 슬롯은 현재 선택된 변환 연산을 적용하여 각 RenderArea 위젯을 업데이트하며, 사용자가 선택한 연산을 변경할 때마다 호출됩니다. shapeSelected() 슬롯은 사용자가 선호하는 도형을 변경할 때마다 RenderArea 위젯의 도형을 업데이트합니다.
private:
void setupShapes();
enum { NumTransformedAreas = 3 };
RenderArea *originalRenderArea;
RenderArea *transformedRenderAreas[NumTransformedAreas];
QComboBox *shapeComboBox;
QComboBox *operationComboBoxes[NumTransformedAreas];
QList<QPainterPath> shapes;
};또한 Window 위젯을 생성할 때 사용되는 비공개 편의 함수인 setupShapes() 를 선언하고, 위젯의 다양한 구성 요소에 대한 포인터를 선언합니다. 사용 가능한 도형은 QPainterPath로 구성된 QList 에 저장하도록 합니다. 또한 QPainter 의 기본 좌표계에서 도형을 렌더링하는 위젯을 제외한, 표시된 RenderArea 위젯의 개수를 세는 비공개 열거형을 선언합니다.
Window 클래스 구현
생성자에서 애플리케이션의 구성 요소를 생성하고 초기화합니다:
Window::Window()
{
originalRenderArea = new RenderArea;
shapeComboBox = new QComboBox;
shapeComboBox->addItem(tr("Clock"));
shapeComboBox->addItem(tr("House"));
shapeComboBox->addItem(tr("Text"));
shapeComboBox->addItem(tr("Truck"));
QGridLayout *layout = new QGridLayout;
layout->addWidget(originalRenderArea, 0, 0);
layout->addWidget(shapeComboBox, 1, 0);먼저, 기본 좌표계에서 도형을 렌더링할 ` RenderArea ` 위젯을 생성합니다. 또한 사용자가 시계, 집, 텍스트, 트럭 등 네 가지 도형 중에서 선택할 수 있도록 하는 관련 ` QComboBox `도 생성합니다. 도형 자체는 생성자의 마지막 부분에서 ` setupShapes() ` 편의 함수를 사용하여 생성됩니다.
for (int i = 0; i < NumTransformedAreas; ++i) {
transformedRenderAreas[i] = new RenderArea;
operationComboBoxes[i] = new QComboBox;
operationComboBoxes[i]->addItem(tr("No transformation"));
operationComboBoxes[i]->addItem(tr("Rotate by 60\xC2\xB0"));
operationComboBoxes[i]->addItem(tr("Scale to 75%"));
operationComboBoxes[i]->addItem(tr("Translate by (50, 50)"));
connect(operationComboBoxes[i], &QComboBox::activated,
this, &Window::operationChanged);
layout->addWidget(transformedRenderAreas[i], 0, i + 1);
layout->addWidget(operationComboBoxes[i], 1, i + 1);
}그런 다음 좌표 변환을 통해 도형을 렌더링할 ` RenderArea ` 위젯을 생성합니다. 기본적으로 적용되는 연산은 ` No Transformation`이며, 즉 도형은 기본 좌표계 내에서 렌더링됩니다. 전역 ` Operation ` 열거형에 정의된 다양한 변환 연산에 해당하는 항목을 사용하여 관련 ` QComboBox` 객체를 생성하고 초기화합니다.
또한 사용자가 선택한 변환 연산이 변경될 때마다 애플리케이션을 업데이트하기 위해, ` QComboBox` 객체들의 ` activated()` 신호를 ` operationChanged() ` 슬롯에 연결합니다.
setLayout(layout);
setupShapes();
shapeSelected(0);
setWindowTitle(tr("Transformations"));
}마지막으로, ` QWidget::setLayout()` 함수를 사용하여 애플리케이션 창의 레이아웃을 설정하고, 비공개 편의 함수 ` setupShapes() `를 사용하여 사용 가능한 도형을 생성하며, 창 제목을 설정하기 전에 공용 슬롯 ` shapeSelected() `를 사용하여 애플리케이션이 시작될 때 시계 도형을 표시하도록 합니다.
void Window::setupShapes()
{
QPainterPath truck;
QPainterPath clock;
QPainterPath house;
QPainterPath text;
...
shapes.append(clock);
shapes.append(house);
shapes.append(text);
shapes.append(truck);
connect(shapeComboBox, &QComboBox::activated,
this, &Window::shapeSelected);
}setupShapes() 함수는 생성자에서 호출되며, 애플리케이션에서 사용되는 도형을 나타내는 QPainterPath 객체를 생성합니다. 생성 과정에 대한 자세한 내용은 painting/transformations/window.cpp 예제 파일을 참조하십시오. 도형들은 QList 에 저장됩니다. QList::append() 함수는 주어진 도형을 목록의 끝에 삽입합니다.
또한, 사용자가 선호하는 도형을 변경할 때 애플리케이션이 업데이트되도록, 관련 QComboBox 의 activated() 신호를 shapeSelected() 슬롯에 연결합니다.
void Window::operationChanged()
{
static const Operation operationTable[] = {
NoTransformation, Rotate, Scale, Translate
};
QList<Operation> operations;
for (int i = 0; i < NumTransformedAreas; ++i) {
int index = operationComboBoxes[i]->currentIndex();
operations.append(operationTable[index]);
transformedRenderAreas[i]->setOperations(operations);
}
}사용자가 선택된 연산(selected operations)을 변경할 때마다 공개 operationChanged() 슬롯이 호출됩니다.
관련된 QComboBoxes 를 쿼리하여 변형된 각 RenderArea 위젯에 대해 선택된 변형 연산을 가져옵니다. 변형된 RenderArea 위젯은 왼쪽에 있는 RenderArea 위젯에 적용된 모든 변형 외에도, 관련 콤보박스에 지정된 변형을 적용하여 도형을 렌더링해야 합니다. 이러한 이유로, 쿼리하는 각 위젯에 대해 해당 연산을 변환 목록( QList )에 추가한 후, 다음 위젯으로 넘어가기 전에 해당 위젯에 이 변환 목록을 적용합니다.
void Window::shapeSelected(int index)
{
QPainterPath shape = shapes[index];
originalRenderArea->setShape(shape);
for (int i = 0; i < NumTransformedAreas; ++i)
transformedRenderAreas[i]->setShape(shape);
}사용자가 선호하는 도형을 변경할 때마다 ` shapeSelected() ` 슬롯이 호출되며, 이때 ` RenderArea ` 위젯의 ` setShape() ` 공개 함수를 사용하여 해당 위젯을 업데이트합니다.
요약
'변환(Transformations)' 예제는 변환이 QPainter 가 그래픽 프리미티브를 렌더링하는 방식에 어떤 영향을 미치는지 보여줍니다. 일반적으로 QPainter 는 장치 고유의 좌표계에서 작동하지만, 좌표 변환도 잘 지원합니다. '변환' 애플리케이션을 사용하면 QPainter 의 좌표계를 확대/축소, 회전 및 평행 이동할 수 있습니다. 이러한 변환이 적용되는 순서는 결과에 매우 중요합니다.
모든 변환 작업은 QPainter 의 변환 행렬을 기반으로 수행됩니다. 변환 행렬에 대한 자세한 내용은 ‘좌표계(Coordinate System )’ 및 ‘ QTransform ’ 문서를 참조하십시오.
Qt 참조 문서에는 여러 가지 그리기 예제가 제공됩니다. 그중에는 Qt가 그리기 작업에 변환을 적용하는 기능을 보여주는 ‘아핀 변환(Affine Transformations )’ 예제가 있습니다. 이 예제를 통해 사용자는 다양한 변환 작업을 직접 실험해 볼 수 있습니다.
© 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.