블록 이동
'블록 이동' 예제는 사용자 정의 전환 효과를 적용한 QStateMachine 를 사용하여 QGraphicsScene 내의 항목을 애니메이션으로 움직이는 방법을 보여줍니다.

이 예제는 위 이미지에서 볼 수 있는 파란색 블록에 애니메이션을 적용합니다. 이 애니메이션은 블록을 네 개의 미리 설정된 위치 사이로 이동시킵니다.
이 예제는 다음 클래스로 구성됩니다.
StateSwitcherQState 를 상속받으며, 다른 상태에 를 추가할 수 있습니다. 해당 상태로 진입하면 이 상태들 중 하나로 무작위로 전환됩니다.StateSwitchTransitionStateSwitchTransitionStateSwitchEvent에서 트리거되는 사용자 정의 전환입니다.StateSwitchEvent는StateSwitchTransition을 트리거하는 QEvent 입니다.QGraphicsRectWidget는 단순히 배경을 blue 단색으로 채우는 QGraphicsWidget 입니다.
이 블록들은 ` QGraphicsRectWidget `의 인스턴스이며, ` QGraphicsScene` 내에서 애니메이션이 적용됩니다. 이를 위해 상태 그래프를 구축하고, 그 안에 애니메이션을 삽입합니다. 그런 다음 이 그래프는 ` QStateMachine`에서 실행됩니다. 이 모든 작업은 ` main()`에서 수행됩니다. 먼저 ` main() ` 함수를 살펴보겠습니다.
main() 함수
QApplication 가 초기화된 후, QGraphicsScene 를 해당 QGraphicsRectWidget들과 함께 설정합니다.
auto button1 = new QGraphicsRectWidget;
auto button2 = new QGraphicsRectWidget;
auto button3 = new QGraphicsRectWidget;
auto button4 = new QGraphicsRectWidget;
button2->setZValue(1);
button3->setZValue(2);
button4->setZValue(3);
QGraphicsScene scene(0, 0, 300, 300);
scene.setBackgroundBrush(Qt::black);
scene.addItem(button1);
scene.addItem(button2);
scene.addItem(button3);
scene.addItem(button4);QGraphicsView 에 장면을 추가한 후에는 상태 그래프를 구축할 차례입니다. 먼저 우리가 구축하려는 상태 차트를 살펴보겠습니다.

group 에는 7개의 하위 상태가 있지만, 다이어그램에는 그중 3개만 포함시켰다는 점에 유의하십시오. 이 그래프를 구축하는 코드를 한 줄씩 살펴보면서 그래프가 어떻게 작동하는지 확인해 보겠습니다. 먼저, group 상태를 구성합니다:
QStateMachine machine;
auto group = new QState;
group->setObjectName("group");
QTimer timer;
timer.setInterval(1250);
timer.setSingleShot(true);
QObject::connect(group, &QState::entered, &timer, QOverload<>::of(&QTimer::start));타이머는 블록이 이동할 때마다 지연 시간을 추가하는 데 사용됩니다. 타이머는 ‘ group ’ 상태로 진입할 때 시작됩니다. 나중에 보게 되겠지만, ‘ group ’ 상태는 타이머가 종료되면 ‘ StateSwitcher ’ 상태로 되돌아가는 전환 경로를 가지고 있습니다. ‘ group ’는 이 상태 기계의 초기 상태이므로, 예제가 시작되면 애니메이션이 예약됩니다.
auto state1 = createGeometryState(button1, QRect(100, 0, 50, 50),
button2, QRect(150, 0, 50, 50),
button3, QRect(200, 0, 50, 50),
button4, QRect(250, 0, 50, 50),
group);
...
auto state7 = createGeometryState(button1, QRect(0, 0, 50, 50),
button2, QRect(250, 0, 50, 50),
button3, QRect(0, 250, 50, 50),
button4, QRect(250, 250, 50, 50),
group);
group->setInitialState(state1);createGeometryState() QState 를 반환하며, 이는 진입 시 항목의 기하학적 속성을 설정합니다. 또한 이 상태의 부모로 를 지정합니다. group
전환에 삽입된 QPropertyAnimation 는 QState 에 할당된 값( QState::assignProperty()을 통해)을 사용하며, 즉 애니메이션은 속성의 현재 값과 대상 상태의 값 사이를 보간합니다. 애니메이션 전환은 나중에 상태 그래프에 추가합니다.
QParallelAnimationGroup animationGroup;
auto anim = new QPropertyAnimation(button4, "geometry");
anim->setDuration(1000);
anim->setEasingCurve(QEasingCurve::OutElastic);
animationGroup.addAnimation(anim);항목들을 병렬로 이동시킵니다. 각 항목은 animationGroup 에 추가되며, 이는 전환에 삽입되는 애니메이션입니다.
auto subGroup = new QSequentialAnimationGroup(&animationGroup);
subGroup->addPause(100);
anim = new QPropertyAnimation(button3, "geometry");
anim->setDuration(1000);
anim->setEasingCurve(QEasingCurve::OutElastic);
subGroup->addAnimation(anim);순차적 애니메이션 그룹인 ` subGroup`은 각 항목의 애니메이션 사이에 지연 시간을 삽입하는 데 도움이 됩니다.
auto stateSwitcher = new StateSwitcher(&machine);
stateSwitcher->setObjectName("stateSwitcher");
group->addTransition(&timer, &QTimer::timeout, stateSwitcher);
stateSwitcher->addState(state1, &animationGroup);
stateSwitcher->addState(state2, &animationGroup);
...
stateSwitcher->addState(state7, &animationGroup);StateSwitcher::addState() 내의 상태 전환기에 StateSwitchTransition이 추가됩니다. 또한 이 함수 내에서 애니메이션을 추가합니다. QPropertyAnimation 가 상태의 값을 사용하므로, 모든 StateSwitchTransition에 동일한 QPropertyAnimation 인스턴스를 삽입할 수 있습니다.
앞서 언급한 바와 같이, 타이머가 만료될 때 트리거되는 전환을 상태 전환기에 추가합니다.
machine.addState(group);
machine.setInitialState(group);
machine.start();마지막으로, 상태 머신을 생성하고 초기 상태를 추가한 뒤 상태 그래프의 실행을 시작할 수 있습니다.
createGeometryState() 함수
createGeometryState() 에서 각 그래픽 항목의 지오메트리를 설정합니다.
static QState *createGeometryState(QObject *w1, const QRect &rect1, QObject *w2, const QRect &rect2,
QObject *w3, const QRect &rect3, QObject *w4, const QRect &rect4,
QState *parent)
{
auto result = new QState(parent);
result->assignProperty(w1, "geometry", rect1);
result->assignProperty(w2, "geometry", rect2);
result->assignProperty(w3, "geometry", rect3);
result->assignProperty(w4, "geometry", rect4);
return result;
}앞서 언급한 바와 같이, ` QAbstractTransition `는 ` assignProperty()`로 설정된 속성 값을 사용하여 ` addAnimation()`로 추가된 애니메이션을 설정합니다.
StateSwitcher 클래스
StateSwitcher 에는 createGeometryState() 를 통해 생성한 각 QState에 대한 상태 전환이 포함되어 있습니다. 이 클래스의 역할은 해당 상태에 진입할 때 무작위로 이러한 상태 중 하나로 전환하는 것입니다.
StateSwitcher 의 모든 함수는 인라인으로 구현되어 있습니다. 이제 그 정의를 단계별로 살펴보겠습니다.
class StateSwitcher : public QState
{
Q_OBJECT
public:
explicit StateSwitcher(QStateMachine *machine) : QState(machine) { }StateSwitcher 는 특정 목적을 위해 설계된 상태이며, 항상 최상위 상태가 됩니다. 우리는 ` m_stateCount `을 사용하여 관리 중인 상태의 개수를 추적하고, ` m_lastIndex `을 사용하여 마지막으로 전환했던 상태가 무엇인지 기억합니다.
void onEntry(QEvent *) override
{
int n;
while ((n = QRandomGenerator::global()->bounded(m_stateCount) + 1) == m_lastIndex)
{ }
m_lastIndex = n;
machine()->postEvent(new StateSwitchEvent(n));
}
void onExit(QEvent *) override {}전환할 다음 상태를 선택하고, StateSwitchEvent 를 포스트합니다. 그러면 StateSwitchTransition 가 선택된 상태로 전환되는 것을 트리거한다는 것을 알고 있습니다.
void addState(QState *state, QAbstractAnimation *animation) {
auto trans = new StateSwitchTransition(++m_stateCount);
trans->setTargetState(state);
addTransition(trans);
trans->addAnimation(animation);
}바로 여기서 마법 같은 일이 일어납니다. 추가된 각 상태에 번호를 할당합니다. 이 번호는 StateSwitchTransition과 StateSwitchEvents 모두에 전달됩니다. 앞서 살펴본 바와 같이, 상태 전환 이벤트는 동일한 번호를 가진 전환을 트리거합니다.
StateSwitchTransition 클래스
StateSwitchTransition 은 ` QAbstractTransition `을 상속받으며 ` StateSwitchEvent`에서 트리거됩니다. 이 클래스에는 인라인 함수만 포함되어 있으므로, 우리가 정의한 유일한 함수인 ` eventTest()` 함수를 살펴보겠습니다.
bool eventTest(QEvent *event) override
{
return (event->type() == QEvent::Type(StateSwitchEvent::StateSwitchType))
&& (static_cast<StateSwitchEvent *>(event)->rand() == m_rand);
}eventTest QStateMachine 는 전환을 트리거해야 하는지 확인할 때 이 함수를 호출하며, 반환 값이 true이면 전환이 실행됩니다. 우리는 할당된 번호가 이벤트의 번호와 동일한지 확인하기만 하면 됩니다(동일한 경우 전환을 실행합니다).
StateSwitchEvent 클래스
StateSwitchEvent 는 ` QEvent`를 상속받으며, ` StateSwitcher`를 통해 상태 및 상태 전환에 할당된 번호를 보관합니다. 이 클래스가 ` StateSwitchTransition`를 트리거하는 데 어떻게 사용되는지는 ` StateSwitcher`에서 이미 살펴보았습니다.
class StateSwitchEvent: public QEvent
{
public:
explicit StateSwitchEvent(int rand) : QEvent(StateSwitchType), m_rand(rand) { }
static constexpr QEvent::Type StateSwitchType = QEvent::Type(QEvent::User + 256);
int rand() const { return m_rand; }
private:
int m_rand;
};이 클래스에는 인라인 함수만 있으므로, 정의만 살펴보면 충분합니다.
QGraphicsRectWidget 클래스
QGraphicsRectWidget은 QGraphicsWidget 을 상속받으며, 단순히 rect()을 파란색으로 그립니다. 우리가 정의한 유일한 함수인 paintEvent()을 인라인으로 처리합니다. 다음은 QGraphicsRectWidget 클래스 정의입니다:
class QGraphicsRectWidget : public QGraphicsWidget
{
public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
painter->fillRect(rect(), Qt::blue);
}
};다음 단계
이 예제에서 보여준 기법은 모든 QPropertyAnimation에 똑같이 잘 적용됩니다. 애니메이션 처리할 값이 Qt 속성인 한, 해당 속성에 대한 애니메이션을 상태 그래프에 삽입할 수 있습니다.
QState::addAnimation()은 QAbstractAnimation 를 매개변수로 받으므로, 어떤 유형의 애니메이션이든 그래프에 삽입할 수 있습니다.
© 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.