변형 예제
변형 예제는 변형이 QPainter 에서 그래픽 기본 요소를 렌더링하는 방식에 어떤 영향을 미치는지 보여줍니다.
이 애플리케이션을 사용하면 QPainter 좌표계의 이동, 회전 및 배율을 변경하여 도형의 렌더링을 조작할 수 있습니다.
이 예제는 두 개의 클래스와 전역 열거형으로 구성됩니다:
RenderArea
클래스는 주어진 도형의 렌더링을 제어합니다.Window
클래스는 애플리케이션의 기본 창입니다.Operation
열거형은 애플리케이션에서 사용할 수 있는 다양한 변환 작업을 설명합니다.
먼저 Operation
열거형을 간단히 살펴본 다음 RenderArea
클래스를 검토하여 도형이 어떻게 렌더링되는지 살펴보겠습니다. 마지막으로 Window
클래스에서 구현된 변형 애플리케이션의 기능을 살펴보겠습니다.
변환 작업
일반적으로 QPainter 은 연결된 장치의 자체 좌표계에서 작동하지만 좌표 변환도 잘 지원합니다.
페인트 장치의 기본 좌표계는 왼쪽 상단 모서리에 원점이 있습니다. X 값은 오른쪽으로 증가하고 Y 값은 아래쪽으로 증가합니다. QPainter::scale () 함수를 사용하여 지정된 오프셋만큼 좌표계의 크기를 조정할 수 있고, QPainter::rotate() 함수를 사용하여 시계 방향으로 회전할 수 있으며, QPainter::translate() 함수를 사용하여 변환(즉, 포인트에 지정된 오프셋을 더하는 것)할 수 있습니다. QPainter::shear () 함수를 사용하여 원점을 중심으로 좌표계를 비틀 수도 있습니다(시어링이라고 함).
모든 변환 작업은 QPainter::worldTransform() 함수를 사용하여 검색할 수 있는 QPainter 의 변환 행렬에서 작동합니다. 행렬은 평면의 한 점을 다른 점으로 변환합니다. 변환 행렬에 대한 자세한 내용은 좌표계 및 QTransform 문서를 참조하세요.
enum Operation { NoTransformation, Translate, Rotate, Scale };
글로벌 Operation
열거형은 renderarea.h
파일에 선언되어 있으며 변환 애플리케이션에서 사용할 수 있는 다양한 변환 작업을 설명합니다.
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")); }
생성자에서는 부모 매개변수를 베이스 클래스에 전달하고 좌표를 렌더링하는 데 사용할 글꼴을 사용자 정의합니다. 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()
함수에서는 새로운 값을 저장하여 RenderArea
위젯을 업데이트한 다음 Qt가 메인 이벤트 루프로 돌아올 때 처리할 페인트 이벤트를 예약하는 QWidget::update() 슬롯을 호출합니다.
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() 함수를 사용하여 다시 칠해야 하는 영역을 지웁니다.
또한 좌표계를 일정한 오프셋으로 변환하여 원본 모양이 적절한 여백을 두고 렌더링되도록 합니다.
painter.save(); transformPainter(painter); drawShape(painter); painter.restore();
도형 렌더링을 시작하기 전에 QPainter::save() 함수를 호출합니다.
QPainter::save() 함수는 현재 좌표계를 포함하여 현재 페인터 상태를 저장합니다(즉, 상태를 스택에 푸시합니다). 페인터 상태를 저장하는 이유는 다음 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 의 기본 그리기 작업과 기본 그래픽 프리미티브를 표시하는 방법에 대한 자세한 내용은 기본 그리기 예제를 참조하세요.
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
클래스는 변환 애플리케이션의 기본 창입니다.
이 애플리케이션은 4개의 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()
를 선언하고 위젯의 다양한 컴포넌트에 대한 포인터를 선언합니다. QList QPainter 또한 QPainterPath의 기본 좌표계에서 도형을 렌더링하는 위젯을 제외하고 표시되는 RenderArea
위젯의 수를 계산하는 비공개 열거형을 선언합니다.
창 클래스 구현
생성자에서는 애플리케이션의 컴포넌트를 생성하고 초기화합니다:
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을 생성하고 초기화합니다.
또한 QComboBoxes의 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); } }
공용 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()
슬롯은 사용자가 선호하는 모양을 변경할 때마다 호출되며, 공개 setShape()
함수를 사용하여 RenderArea
위젯을 업데이트합니다.
요약
변형 예제는 변형이 QPainter 그래픽 프리미티브를 렌더링하는 방식에 어떤 영향을 미치는지 보여줍니다. 일반적으로 QPainter 은 기기의 자체 좌표계에서 작동하지만 좌표 변환도 잘 지원합니다. 변환 애플리케이션을 사용하면 QPainter 의 좌표계를 크기 조정, 회전 및 변환할 수 있습니다. 이러한 변환을 적용하는 순서는 결과에 필수적입니다.
모든 변환 작업은 QPainter 의 변환 매트릭스에서 작동합니다. 변환 행렬에 대한 자세한 내용은 좌표계 및 QTransform 문서를 참조하세요.
Qt 참조 문서는 몇 가지 그림 예제를 제공합니다. 그중에는 페인팅 연산에서 변환을 수행하는 Qt의 기능을 보여주는 아핀 변환 예제가 있습니다. 이 예제를 통해 사용자는 다양한 변환 연산을 실험해 볼 수 있습니다.
© 2025 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.