이 페이지에서

드래그 앤 드롭 로봇 예제

그래픽 뷰에서 항목을 드래그 앤 드롭하는 방법을 보여줍니다.

'드래그 앤 드롭 로봇' 예제는 ` QGraphicsItem `의 서브클래스에서 드래그 앤 드롭을 구현하는 방법과, Qt의 애니메이션 프레임워크를 사용하여 항목에 애니메이션을 적용하는 방법을 보여줍니다.

드래그 앤 드롭 이벤트에 반응하는 로봇의 적용

Graphics View는 ` QGraphicsItem ` 클래스에서 파생된 다수의 사용자 정의 2D 그래픽 항목을 관리하고 상호 작용하기 위한 ` QGraphicsScene ` 클래스와, 확대/축소 및 회전 기능을 지원하는 항목 시각화용 ` QGraphicsView ` 위젯을 제공합니다.

이 예제는 ` Robot ` 클래스, ` ColorItem ` 클래스, 그리고 `main` 함수로 구성됩니다. ` Robot ` 클래스는 ` RobotHead ` 및 ` RobotLimb`을 포함하여 ` RobotPart `에서 파생된 여러 사지로 구성된 간단한 로봇을 정의하며, ` ColorItem ` 클래스는 드래그 가능한 색상 타원을 제공하고, ` main() ` 함수는 메인 애플리케이션 창을 제공합니다.

먼저 Robot 클래스를 살펴보며, QPropertyAnimation 를 사용하여 각 부품을 개별적으로 회전하고 애니메이션을 적용할 수 있도록 구성하는 방법을 알아보고, 이어서 ColorItem 클래스를 검토하여 항목 간 드래그 앤 드롭을 구현하는 방법을 시연해 보겠습니다. 마지막으로 main() 함수를 살펴보며 모든 구성 요소를 통합하여 최종 애플리케이션을 완성하는 방법을 확인해 보겠습니다.

Robot 클래스 정의

로봇은 세 가지 주요 클래스, 즉 ` RobotHead`, ` RobotTorso`, 그리고 상·하반신과 팔을 구성하는 ` RobotLimb`로 이루어져 있습니다. 모든 부품은 ` RobotPart ` 클래스에서 파생되며, 이 클래스는 다시 ` QGraphicsObject`를 상속받습니다. ` Robot ` 클래스 자체는 시각적인 형태가 없으며, 로봇의 루트 노드 역할만 수행합니다.

RobotPart 클래스 선언부터 살펴보겠습니다.

class RobotPart : public QGraphicsObject
{
public:
    RobotPart(QGraphicsItem *parent = nullptr);

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
    void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

    QColor color = Qt::lightGray;
    bool dragOver = false;
};

이 기본 클래스는 ` QGraphicsObject`을 상속받습니다. ` QGraphicsObject `은 ` QObject`을 상속받아 시그널과 슬롯을 제공하며, ` Q_PROPERTY`을 사용하여 ` QGraphicsItem`의 속성을 선언함으로써 ` QPropertyAnimation`에서 해당 속성에 접근할 수 있게 합니다.

RobotPart는 또한 드롭 이벤트를 수신하기 위한 세 가지 가장 중요한 이벤트 핸들러인 dragEnterEvent(), dragLeaveEvent(), dropEvent()을 구현합니다.

색상은 멤버 변수로 저장되며, dragOver 변수와 함께 사용됩니다. 이 변수는 나중에 사지가 드래그되어 올려진 색상을 수용할 수 있음을 시각적으로 표시하는 데 사용될 것입니다.

RobotPart::RobotPart(QGraphicsItem *parent)
    : QGraphicsObject(parent), color(Qt::lightGray)
{
    setAcceptDrops(true);
}

RobotPart의 생성자는 dragOver 멤버를 초기화하고 색상을 Qt::lightGray 로 설정합니다. 생성자 본문에서는 setAcceptDrops(true)를 호출하여 드롭 이벤트 수신 기능을 활성화합니다.

이 클래스의 나머지 구현은 드래그 앤 드롭을 지원하기 위한 것입니다.

void RobotPart::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasColor()) {
        event->setAccepted(true);
        dragOver = true;
        update();
    } else {
        event->setAccepted(false);
    }
}

dragEnterEvent() 핸들러는 드래그 앤 드롭 요소가 로봇 부품 영역으로 드래그될 때 호출됩니다.

이 핸들러 구현은 해당 항목 전체가 들어오는 드래그 객체와 연관된 MIME 데이터를 수락할 수 있는지 여부를 결정합니다. RobotPart 는 색상 드롭을 수락하는 모든 파트를 위한 기본 동작을 제공합니다. 따라서 들어오는 드래그 객체에 색상이 포함되어 있으면 이벤트가 수락되고, 사용자에게 긍정적인 시각적 피드백을 제공하기 위해 ` dragOver `을 ` true `로 설정하고 `update()`를 호출합니다. 그렇지 않으면 이벤트가 무시되어 이벤트가 상위 요소로 전파됩니다.

void RobotPart::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event);
    dragOver = false;
    update();
}

dragLeaveEvent() 핸들러는 드래그 앤 드롭 요소가 로봇 파트 영역 밖으로 끌려 나갈 때 호출됩니다. 우리의 구현은 단순히 dragOver를 false로 재설정하고 update()를 호출하여 드래그가 이 항목을 떠났다는 시각적 피드백을 제공합니다.

void RobotPart::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    dragOver = false;
    if (event->mimeData()->hasColor())
        color = qvariant_cast<QColor>(event->mimeData()->colorData());
    update();
}

dropEvent() 핸들러는 드래그 앤 드롭 요소가 항목 위에 놓일 때(즉, 드래그 중에 항목 위에서 마우스 버튼을 놓을 때) 호출됩니다.

dragOver 를 false로 재설정하고, 항목의 새 색상을 할당한 다음 update()를 호출합니다.

RobotHead, RobotTorso 및 RobotLimb 의 선언과 구현은 사실상 동일합니다. RobotHead 에는 사소한 차이점이 하나 있으므로 이 클래스를 자세히 살펴보고, 다른 클래스들에 대해서는 독자의 연습 문제로 남겨두겠습니다.

class RobotHead : public RobotPart
{
public:
    RobotHead(QGraphicsItem *parent = nullptr);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

private:
    QPixmap pixmap;
};

RobotHead 클래스는 RobotPart 을 상속받으며, boundingRect() 및 paint()에 대한 필수 구현을 제공합니다. 또한 이미지 드롭에 대한 특수한 처리를 제공하기 위해 dragEnterEvent() 및 dropEvent()를 재구현합니다.

이 클래스에는 이미지 드롭 수신 기능을 구현하는 데 사용할 수 있는 비공개 pixmap 멤버가 포함되어 있습니다.

RobotHead::RobotHead(QGraphicsItem *parent)
    : RobotPart(parent)
{
}

RobotHead 이 클래스의 생성자는 다소 단순하며, 단순히 RobotPart 의 생성자로 전달할 뿐입니다.

QRectF RobotHead::boundingRect() const
{
    return QRectF(-15, -50, 30, 50);
}

boundingRect()의 재구현은 머리의 크기를 반환합니다. 회전 중심을 아이템의 하단 중앙으로 설정하기 위해, (-15, -50)에서 시작하여 폭 30 단위, 높이 50 단위까지 확장되는 경계 사각형을 선택했습니다. 머리를 회전할 때, “목”은 고정된 상태로 유지되고 머리 윗부분만 좌우로 기울어집니다.

void RobotHead::paint(QPainter *painter,
           const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    if (pixmap.isNull()) {
        painter->setBrush(dragOver ? color.lighter(130) : color);
        painter->drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt::RelativeSize);
        painter->setBrush(Qt::white);
        painter->drawEllipse(-7, -3 - 20, 7, 7);
        painter->drawEllipse(0, -3 - 20, 7, 7);
        painter->setBrush(Qt::black);
        painter->drawEllipse(-5, -1 - 20, 2, 2);
        painter->drawEllipse(2, -1 - 20, 2, 2);
        painter->setPen(QPen(Qt::black, 2));
        painter->setBrush(Qt::NoBrush);
        painter->drawArc(-6, -2 - 20, 12, 15, 190 * 16, 160 * 16);
    } else {
        painter->scale(.2272, .2824);
        painter->drawPixmap(QPointF(-15 * 4.4, -50 * 3.54), pixmap);
    }
}

paint()에서는 실제 머리를 그립니다. 구현은 두 부분으로 나뉩니다. 머리에 이미지가 드래그 앤 드롭된 경우 해당 이미지를 그리고, 그렇지 않은 경우 간단한 벡터 그래픽을 사용하여 둥근 직사각형 모양의 로봇 머리를 그립니다.

성능상의 이유로, 그려지는 내용의 복잡도에 따라 일련의 벡터 연산을 사용하는 것보다 머리를 이미지로 그리는 것이 더 빠른 경우가 많습니다.

void RobotHead::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasImage()) {
        event->setAccepted(true);
        dragOver = true;
        update();
    } else {
        RobotPart::dragEnterEvent(event);
    }
}

로봇 머리는 이미지를 드롭할 수 있습니다. 이를 지원하기 위해, dragEnterEvent()의 재구현에서는 드래그 객체에 이미지 데이터가 포함되어 있는지 확인하고, 포함되어 있다면 이벤트를 수락합니다. 그렇지 않은 경우 기본 RobotPart 구현으로 되돌아갑니다.

void RobotHead::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasImage()) {
        dragOver = false;
        pixmap = qvariant_cast<QPixmap>(event->mimeData()->imageData());
        update();
    } else {
        RobotPart::dropEvent(event);
    }
}

이미지 지원을 이어가기 위해 dropEvent()도 구현해야 합니다. 드래그 객체에 이미지 데이터가 포함되어 있는지 확인하고, 포함되어 있다면 이 데이터를 멤버 pixmap으로 저장한 후 update()를 호출합니다. 이 pixmap은 앞서 살펴본 paint() 구현 내부에서 사용됩니다.

RobotTorso RobotLimb 와 는 와 유사하므로, 바로 클래스로 넘어가겠습니다. RobotHead Robot

class Robot : public RobotPart
{
public:
    Robot(QGraphicsItem *parent = nullptr);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
};

Robot 클래스도 RobotPart 를 상속받으며, 다른 부분들과 마찬가지로 boundingRect() 및 paint()을 구현합니다. 다만 이 클래스는 다소 특별한 구현 방식을 제공합니다:

QRectF Robot::boundingRect() const
{
    return QRectF();
}

void Robot::paint(QPainter *painter,
                  const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

Robot 클래스는 로봇의 나머지 부분에 대한 기본 노드로만 사용되기 때문에 시각적 표현이 없습니다. 따라서 이 클래스의 boundingRect() 구현은 null 값의 QRectF 을 반환할 수 있으며, paint() 함수는 아무 작업도 수행하지 않습니다.

Robot::Robot(QGraphicsItem *parent)
    : RobotPart(parent)
{
    setFlag(ItemHasNoContents);

    QGraphicsObject *torsoItem = new RobotTorso(this);
    QGraphicsObject *headItem = new RobotHead(torsoItem);
    QGraphicsObject *upperLeftArmItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerLeftArmItem = new RobotLimb(upperLeftArmItem);
    QGraphicsObject *upperRightArmItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerRightArmItem = new RobotLimb(upperRightArmItem);
    QGraphicsObject *upperRightLegItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerRightLegItem = new RobotLimb(upperRightLegItem);
    QGraphicsObject *upperLeftLegItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerLeftLegItem = new RobotLimb(upperLeftLegItem);

생성자는 먼저 ItemHasNoContents 플래그를 설정하는 것으로 시작하는데, 이는 시각적 표현이 없는 항목에 대한 사소한 최적화입니다.

그런 다음 모든 로봇 부품(머리, 몸통, 상·하완 및 다리)을 생성합니다. 스택 순서는 매우 중요하며, 요소들이 올바르게 회전하고 움직이도록 보장하기 위해 부모-자식 계층 구조를 사용합니다. 몸통은 루트 요소이므로 먼저 생성합니다. 그런 다음 머리를 생성하고 몸통을 ` HeadItem`의 생성자에 전달합니다. 이렇게 하면 머리가 몸통의 자식이 되며, 몸통을 회전시키면 머리도 따라 회전합니다. 나머지 사지에도 동일한 패턴이 적용됩니다.

    headItem->setPos(0, -18);
    upperLeftArmItem->setPos(-15, -10);
    lowerLeftArmItem->setPos(30, 0);
    upperRightArmItem->setPos(15, -10);
    lowerRightArmItem->setPos(30, 0);
    upperRightLegItem->setPos(10, 32);
    lowerRightLegItem->setPos(30, 0);
    upperLeftLegItem->setPos(-10, 32);
    lowerLeftLegItem->setPos(30, 0);

각 로봇 부품은 신중하게 배치됩니다. 예를 들어, 왼쪽 상단 팔은 몸통의 왼쪽 상단 영역으로 정확하게 이동되고, 오른쪽 상단 팔은 오른쪽 상단 영역으로 이동됩니다.

    QParallelAnimationGroup *animation = new QParallelAnimationGroup(this);

    QPropertyAnimation *headAnimation = new QPropertyAnimation(headItem, "rotation");
    headAnimation->setStartValue(20);
    headAnimation->setEndValue(-20);
    QPropertyAnimation *headScaleAnimation = new QPropertyAnimation(headItem, "scale");
    headScaleAnimation->setEndValue(1.1);
    animation->addAnimation(headAnimation);
    animation->addAnimation(headScaleAnimation);

다음 섹션에서는 모든 애니메이션 오브젝트를 생성합니다. 이 코드 조각은 머리의 크기와 회전을 제어하는 두 가지 애니메이션을 보여줍니다. 두 개의 ` QPropertyAnimation ` 인스턴스는 단순히 오브젝트, 속성, 그리고 각각의 시작값과 끝값을 설정합니다.

모든 애니메이션은 하나의 최상위 병렬 애니메이션 그룹에 의해 제어됩니다. 크기 및 회전 애니메이션이 이 그룹에 추가됩니다.

나머지 애니메이션들도 비슷한 방식으로 정의됩니다.

    for (int i = 0; i < animation->animationCount(); ++i) {
        QPropertyAnimation *anim = qobject_cast<QPropertyAnimation *>(animation->animationAt(i));
        anim->setEasingCurve(QEasingCurve::SineCurve);
        anim->setDuration(2000);
    }

    animation->setLoopCount(-1);
    animation->start();

마지막으로 각 애니메이션에 이징 커브와 지속 시간을 설정하고, 최상위 애니메이션 그룹이 무한히 반복되도록 한 다음, 최상위 애니메이션을 시작합니다.

ColorItem 클래스 정의

ColorItem 클래스는 누르면 로봇 부품 위로 색상을 끌어다 놓을 수 있는 원형 항목을 나타냅니다.

class ColorItem : public QGraphicsItem
{
public:
    ColorItem();

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;

private:
    QColor color;
};

이 클래스는 매우 간단합니다. 애니메이션을 사용하지 않으며, 속성이나 시그널 및 슬롯이 필요하지 않으므로, 리소스를 절약하기 위해 QGraphicsObject 가 아닌 QGraphicsItem 를 상속받는 것이 가장 자연스럽습니다.

이 클래스는 필수 함수인 boundingRect()과 paint()을 선언하고, mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent()의 재구현을 추가합니다. 또한 하나의 비공개 color 멤버를 포함하고 있습니다.

이제 이 클래스의 구현을 살펴보겠습니다.

ColorItem::ColorItem()
    : color(QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256))
{
    setToolTip(QString("QColor(%1, %2, %3)\n%4")
              .arg(color.red()).arg(color.green()).arg(color.blue())
              .arg("Click and drag this color onto the robot!"));
    setCursor(Qt::OpenHandCursor);
    setAcceptedMouseButtons(Qt::LeftButton);
}

ColorItem이 클래스의 생성자는 ` QRandomGenerator`를 활용하여 color 멤버에 불투명한 무작위 색상을 할당합니다. 사용 편의성을 높이기 위해 사용자에게 유용한 힌트를 제공하는 툴팁을 할당하고, 적절한 커서를 설정합니다. 이를 통해 마우스 포인터가 항목 위로 이동할 때 커서가 ` Qt::OpenHandCursor `로 변경되도록 보장합니다.

마지막으로, setAcceptedMouseButtons()를 호출하여 이 항목이 Qt::LeftButton 만 처리할 수 있도록 보장합니다. 이렇게 하면 항상 마우스 왼쪽 버튼만 눌리고 놓인다고 가정할 수 있으므로, 마우스 이벤트 핸들러를 크게 단순화할 수 있습니다.

QRectF ColorItem::boundingRect() const
{
    return QRectF(-15.5, -15.5, 34, 34);
}

이 항목의 경계 사각형은 항목의 원점(0, 0)을 중심으로 한 고정된 30x30 단위이며, 크기를 조절할 수 있는 펜이 윤곽선을 그릴 수 있도록 모든 방향으로 0.5 단위씩 조정됩니다. 최종적인 시각적 효과를 위해 경계 영역은 간단한 드롭 섀도우를 적용할 공간을 확보하기 위해 아래쪽과 오른쪽 방향으로 몇 유닛씩 조정됩니다.

void ColorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->setPen(Qt::NoPen);
    painter->setBrush(Qt::darkGray);
    painter->drawEllipse(-12, -12, 30, 30);
    painter->setPen(QPen(Qt::black, 1));
    painter->setBrush(QBrush(color));
    painter->drawEllipse(-15, -15, 30, 30);
}

paint() 구현은 1 단위 두께의 검은색 윤곽선, 단색 채우기, 짙은 회색 드롭섀도우를 가진 타원을 그립니다.

void ColorItem::mousePressEvent(QGraphicsSceneMouseEvent *)
{
    setCursor(Qt::ClosedHandCursor);
}

mousePressEvent() 핸들러는 항목 영역 내에서 마우스 버튼을 누를 때 호출됩니다. 이 구현에서는 커서를 Qt::ClosedHandCursor 로 설정하기만 합니다.

void ColorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *)
{
    setCursor(Qt::OpenHandCursor);
}

mouseReleaseEvent() 핸들러는 항목 영역 내에서 마우스 버튼을 눌렀다가 놓을 때 호출됩니다. 이 구현에서는 커서를 다시 Qt::OpenHandCursor 로 설정합니다. 마우스 클릭 및 릴리스 이벤트 핸들러는 함께 사용자에게 유용한 시각적 피드백을 제공합니다. CircleItem 위로 마우스 포인터를 이동하면 커서가 열린 손 모양으로 바뀝니다. 항목을 클릭하면 닫힌 손 모양 커서가 표시됩니다. 마우스 버튼을 놓으면 다시 열린 손 모양 커서로 돌아갑니다.

void ColorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
        .length() < QApplication::startDragDistance()) {
        return;
    }

    QDrag *drag = new QDrag(event->widget());
    QMimeData *mime = new QMimeData;
    drag->setMimeData(mime);

mouseMoveEvent() 핸들러는 ColorItem 영역 내에서 마우스 버튼을 누른 후 마우스를 움직일 때 호출됩니다. 이 구현은 CircleItem 에 있어 가장 중요한 로직, 즉 드래그를 시작하고 관리하는 코드를 제공합니다.

이 구현은 먼저 마우스가 마우스 지터 노이즈를 제거할 만큼 충분히 멀리 드래그되었는지 확인하는 것으로 시작합니다. 마우스가 애플리케이션의 드래그 시작 거리보다 더 멀리 드래그된 경우에만 드래그를 시작하고자 합니다.

이어서, ` widget ` 이벤트(즉, ` QGraphicsView ` 뷰포트)를 생성자에 전달하여 ` QDrag ` 객체를 생성합니다. Qt는 이 객체가 적절한 시점에 삭제되도록 보장합니다. 또한 색상이나 이미지 데이터를 담을 수 있는 ` QMimeData ` 인스턴스를 생성하고, 이를 드래그 객체에 할당합니다.

    static int n = 0;
    if (n++ > 2 && QRandomGenerator::global()->bounded(3) == 0) {
        QImage image(":/images/head.png");
        mime->setImageData(image);

        drag->setPixmap(QPixmap::fromImage(image).scaled(30, 40));
        drag->setHotSpot(QPoint(15, 30));

이 코드 조각은 다소 무작위적인 결과를 보여줍니다. 가끔씩 드래그 객체의 MIME 데이터에 특수한 이미지가 할당되기도 합니다. 또한 픽스맵도 드래그 객체의 픽스맵으로 할당됩니다. 이렇게 하면 드래그 중인 이미지를 마우스 커서 아래의 픽스맵으로 볼 수 있습니다.

    } else {
        mime->setColorData(color);
        mime->setText(QString("#%1%2%3")
                      .arg(color.red(), 2, 16, QLatin1Char('0'))
                      .arg(color.green(), 2, 16, QLatin1Char('0'))
                      .arg(color.blue(), 2, 16, QLatin1Char('0')));

        QPixmap pixmap(34, 34);
        pixmap.fill(Qt::white);

        QPainter painter(&pixmap);
        painter.translate(15, 15);
        painter.setRenderHint(QPainter::Antialiasing);
        paint(&painter, nullptr, nullptr);
        painter.end();

        pixmap.setMask(pixmap.createHeuristicMask());

        drag->setPixmap(pixmap);
        drag->setHotSpot(QPoint(15, 20));
    }

그렇지 않은 경우, 즉 가장 흔한 결과로는 드래그 객체의 MIME 데이터에 단순한 색상이 할당됩니다. 이 색상( ColorItem )을 새로운 픽스맵으로 렌더링하여 사용자에게 색상이 “드래그되고 있다”는 시각적 피드백을 제공합니다.

    drag->exec();
    setCursor(Qt::OpenHandCursor);
}

마지막으로 드래그를 실행합니다. ` QDrag::exec()`는 이벤트 루프로 다시 진입하며, 드래그가 놓이거나 취소된 경우에만 종료됩니다. 어떤 경우든 커서를 ` Qt::OpenHandCursor`로 재설정합니다.

main() 함수

이제 Robot 및 ColorItem 클래스가 완성되었으므로, main() 함수 내에서 모든 요소를 통합할 수 있습니다.

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

먼저 QApplication 를 생성하고 난수 생성기를 초기화합니다. 이렇게 하면 애플리케이션이 시작될 때마다 색상 항목의 색상이 달라집니다.

    QGraphicsScene scene(-200, -200, 400, 400);

    for (int i = 0; i < 10; ++i) {
        ColorItem *item = new ColorItem;
        item->setPos(::sin((i * 6.28) / 10.0) * 150,
                     ::cos((i * 6.28) / 10.0) * 150);

        scene.addItem(item);
    }

    Robot *robot = new Robot;
    robot->setTransform(QTransform::fromScale(1.2, 1.2), true);
    robot->setPos(0, -20);
    scene.addItem(robot);

고정 크기의 장면을 생성하고, 원형으로 배열된 10개의 ColorItem 인스턴스를 만듭니다. 각 아이템을 장면에 추가합니다.

이 원의 중심에는 Robot 인스턴스를 하나 생성합니다. 로봇의 크기를 조정하고 몇 단위 위쪽으로 이동시킨 다음, 이를 장면에 추가합니다.

    GraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);
    view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    view.setBackgroundBrush(QColor(230, 200, 167));
    view.setWindowTitle("Drag and Drop Robot");
    view.show();

    return app.exec();
}

마지막으로 QGraphicsView 창을 생성하고, 해당 창에 이 장면을 할당합니다.

시각적 품질을 높이기 위해 앤티앨리어싱을 활성화합니다. 또한 시각적 업데이트 처리를 단순화하기 위해 바운딩 사각형 업데이트를 사용하도록 선택합니다. 뷰에는 고정된 모래색 배경과 창 제목이 지정됩니다.

그런 다음 뷰를 표시합니다. 제어권이 이벤트 루프로 진입하는 즉시 애니메이션이 시작됩니다.

예제 프로젝트 @ code.qt.io

© 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.