Qt State Machine C++ 가이드

상태 머신 프레임워크는 상태 그래프를 생성하고 실행하기 위한 클래스를 제공합니다. 이 페이지에서는 프레임워크의 주요 기능을 C++로 설명합니다.

상태 머신 프레임워크의 C++ 클래스

스테이트 머신 프레임워크의 전체 C++ 클래스 목록은 다음을 참조하세요. Qt State Machine C++ Classes

간단한 상태 머신

스테이트 머신 API의 핵심 기능을 설명하기 위해 간단한 예제를 살펴보겠습니다: s1 , s2s3 의 세 가지 상태를 가진 상태 머신입니다. 이 상태 머신은 단일 QPushButton 버튼으로 제어되며, 버튼을 클릭하면 다른 상태로 전환됩니다. 처음에 상태 머신은 s1 상태에 있습니다. 이 머신의 상태 차트는 다음과 같습니다:

다음 스니펫은 이러한 상태 머신을 만드는 데 필요한 코드를 보여줍니다. 먼저 상태 머신과 상태를 생성합니다:

    QStateMachine machine;
    QState *s1 = new QState();
    QState *s2 = new QState();
    QState *s3 = new QState();

그런 다음 QState::addTransition() 함수를 사용하여 트랜지션을 생성합니다:

    s1->addTransition(button, &QPushButton::clicked, s2);
    s2->addTransition(button, &QPushButton::clicked, s3);
    s3->addTransition(button, &QPushButton::clicked, s1);

다음으로, 머신에 상태를 추가하고 머신의 초기 상태를 설정합니다:

    machine.addState(s1);
    machine.addState(s2);
    machine.addState(s3);
    machine.setInitialState(s1);

마지막으로 상태 머신을 시작합니다:

    machine.start();

상태 머신은 비동기적으로 실행됩니다. 즉, 애플리케이션의 이벤트 루프의 일부가 됩니다.

상태 진입과 종료에 유용한 작업 수행하기

위의 상태 머신은 한 상태에서 다른 상태로 전환할 뿐 어떤 작업도 수행하지 않습니다. QState::assignProperty () 함수는 상태가 입력될 때 QObject 의 속성을 설정하는 데 사용할 수 있습니다. 다음 코드 조각에서는 QLabel 의 텍스트 속성에 할당해야 하는 값이 각 상태에 대해 지정되어 있습니다:

    s1->assignProperty(label, "text", "In state s1");
    s2->assignProperty(label, "text", "In state s2");
    s3->assignProperty(label, "text", "In state s3");

상태 중 하나를 입력하면 레이블의 텍스트가 그에 따라 변경됩니다.

상태가 입력되면 QState::entered() 신호가 전송되고 상태가 종료되면 QState::exited() 신호가 전송됩니다. 다음 코드 조각에서는 s3 상태가 입력되면 버튼의 showMaximized() 슬롯이 호출되고 s3 상태가 종료되면 버튼의 showMinimized() 슬롯이 호출됩니다:

    QObject::connect(s3, &QState::entered, button, &QPushButton:showMaximized);
    QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized);

커스텀 스테이트는 QAbstractState::onEntry() 및 QAbstractState::onExit()를 다시 구현할 수 있습니다.

완료되는 상태 머신

이전 섹션에서 정의한 상태 머신은 결코 완료되지 않습니다. 상태 머신이 완료되려면 최상위 최종 상태(QFinalState 객체)가 있어야 합니다. 상태 머신이 최상위 최종 상태에 들어가면 머신은 QStateMachine::finished() 신호를 내보내고 중지됩니다.

그래프에 최종 상태를 도입하려면 QFinalState 객체를 만들어 하나 이상의 트랜지션의 대상으로 사용하기만 하면 됩니다.

상태를 그룹화하여 트랜지션 공유

사용자가 종료 버튼을 클릭하여 언제든지 애플리케이션을 종료할 수 있기를 원한다고 가정해 봅시다. 이를 위해서는 최종 상태를 만들고 이를 종료 버튼의 clicked() 신호와 연결된 전환의 대상으로 만들어야 합니다. s1 , s2s3 각각에서 전환을 추가할 수도 있지만, 이는 중복되는 것처럼 보이며 향후 추가되는 모든 새 상태에서도 이러한 전환을 추가해야 한다는 점을 기억해야 합니다.

상태 s1, s2s3 를 그룹화하여 동일한 동작(즉, 상태 머신이 어떤 상태인지에 관계없이 종료 버튼을 클릭하면 상태 머신이 종료되는 동작)을 구현할 수 있습니다. 이는 새로운 최상위 상태를 만들고 세 개의 원래 상태를 새 상태의 자식으로 만드는 방식으로 수행됩니다. 다음 다이어그램은 새 상태 머신을 보여줍니다.

세 개의 원래 상태는 이제 새로운 최상위 상태인 s1 의 하위 상태임을 반영하여 s11, s12s13 로 이름이 변경되었습니다. 자식 상태는 부모 상태의 트랜지션을 암시적으로 상속합니다. 즉, 이제 s1 에서 최종 상태 s2 로 단일 전환을 추가하는 것으로 충분합니다. s1 에 추가된 새 상태도 이 전환을 자동으로 상속합니다.

상태를 그룹화하는 데 필요한 것은 상태를 만들 때 적절한 부모를 지정하기만 하면 됩니다. 또한 어떤 자식 상태가 초기 상태인지(즉, 부모 상태가 전환의 대상일 때 상태 머신이 들어갈 자식 상태) 지정해야 합니다.

    QState *s1 = new QState();
    QState *s11 = new QState(s1);
    QState *s12 = new QState(s1);
    QState *s13 = new QState(s1);
    s1->setInitialState(s11);
    machine.addState(s1);
    QFinalState *s2 = new QFinalState();
    s1->addTransition(quitButton, &QPushButton::clicked, s2);
    machine.addState(s2);
    machine.setInitialState(s1);

    QObject::connect(&machine, &QStateMachine::finished,
                     QCoreApplication::instance(), &QCoreApplication::quit);

이 경우 상태 머신이 완료되면 애플리케이션이 종료되도록 하려면 머신의 finished() 신호가 애플리케이션의 quit() 슬롯에 연결됩니다.

자식 상태는 상속된 트랜지션을 재정의할 수 있습니다. 예를 들어, 다음 코드는 상태 머신이 s12 상태일 때 종료 버튼이 효과적으로 무시되도록 하는 트랜지션을 추가합니다.

    s12->addTransition(quitButton, &QPushButton::clicked, s12);

즉, 대상 상태가 소스 상태와 상태 계층 구조에서 같은 레벨에 있을 필요는 없습니다.

히스토리 상태를 사용하여 현재 상태 저장 및 복원하기

이전 섹션에서 설명한 예제에 "인터럽트" 메커니즘을 추가하여 사용자가 버튼을 클릭하여 스테이트 머신이 관련 없는 작업을 수행하도록 하고, 그 후 스테이트 머신이 이전에 수행하던 작업을 다시 시작해야 한다고 상상해 보세요(이 경우 s11, s12s13 중 하나인 이전 상태로 돌아가는 것).

이러한 동작은 히스토리 상태를 사용하여 쉽게 모델링할 수 있습니다. 히스토리 상태(QHistoryState 개체)는 부모 상태가 마지막으로 종료되었을 때 부모 상태가 있었던 자식 상태를 나타내는 의사 상태입니다.

히스토리 상태는 현재 자식 상태를 기록하고자 하는 상태의 자식으로 생성되며, 런타임에 상태 머신이 이러한 상태의 존재를 감지하면 부모 상태가 종료될 때 자동으로 현재 (실제) 자식 상태를 기록합니다. 히스토리 상태로의 전환은 사실 상태 머신이 이전에 저장했던 자식 상태로의 전환이며, 상태 머신은 자동으로 실제 자식 상태로 전환을 '전달'합니다.

다음 다이어그램은 인터럽트 메커니즘이 추가된 후의 상태 머신을 보여줍니다.

다음 코드는 이를 구현하는 방법을 보여줍니다. 이 예에서는 s3 을 입력하면 메시지 상자를 표시한 다음 히스토리 상태를 통해 즉시 이전 하위 상태인 s1 으로 돌아갑니다.

    QHistoryState *s1h = new QHistoryState(s1);

    QState *s3 = new QState();
    s3->assignProperty(label, "text", "In s3");
    QMessageBox *mbox = new QMessageBox(mainWindow);
    mbox->addButton(QMessageBox::Ok);
    mbox->setText("Interrupted!");
    mbox->setIcon(QMessageBox::Information);
    QObject::connect(s3, &QState::entered, mbox, &QMessageBox::exec);
    s3->addTransition(s1h);
    machine.addState(s3);

    s1->addTransition(interruptButton, &QPushButton::clicked, s3);

병렬 상태를 사용하여 상태의 조합 폭발을 피하기

자동차의 상호 배타적인 속성 집합을 단일 상태 머신에서 모델링하고 싶다고 가정해 보겠습니다. 우리가 관심 있는 속성이 깨끗함 대 더럽음, 움직임 대 움직이지 않음이라고 가정해 봅시다. 가능한 모든 조합을 표현하고 자유롭게 이동하려면 4개의 상호 배타적인 상태와 8개의 전환이 필요합니다.

세 번째 속성(예: 빨간색 대 파란색)을 추가하면 총 상태 수가 8개로 두 배가 되고, 네 번째 속성(예: 밀폐형 대 전환형)을 추가하면 총 상태 수가 다시 두 배로 늘어나 16개가 됩니다.

병렬 상태를 사용하면 속성을 추가할 때마다 총 상태 및 전환 수가 기하급수적으로 증가하는 것이 아니라 선형적으로 증가합니다. 또한 병렬 상태는 형제 상태에 영향을 주지 않고 상태를 병렬 상태에 추가하거나 제거할 수 있습니다.

병렬 상태 그룹을 만들려면 QState 생성자에 QState::ParallelStates 을 전달합니다.

    QState *s1 = new QState(QState::ParallelStates);
    // s11 and s12 will be entered in parallel
    QState *s11 = new QState(s1);
    QState *s12 = new QState(s1);

병렬 상태 그룹을 입력하면 모든 하위 상태가 동시에 입력됩니다. 개별 하위 상태 내의 트랜지션은 정상적으로 작동합니다. 그러나 자식 상태 중 하나라도 부모 상태를 종료하는 전환을 수행할 수 있습니다. 이 경우 부모 상태와 모든 자식 상태가 종료됩니다.

스테이트 머신 프레임워크의 병렬성은 인터리브 시맨틱을 따릅니다. 모든 병렬 연산은 이벤트 처리의 단일 원자적 단계에서 실행되므로 어떤 이벤트도 병렬 연산을 중단시킬 수 없습니다. 그러나 머신 자체는 단일 스레드이므로 이벤트는 여전히 순차적으로 처리됩니다. 예를 들어 보겠습니다: 동일한 병렬 상태 그룹을 종료하는 두 개의 트랜지션이 있고 그 조건이 동시에 참이 되는 상황을 생각해 보세요. 이 경우 첫 번째 이벤트로 인해 이미 머신이 병렬 상태에서 종료되었으므로 두 이벤트 중 마지막에 처리되는 이벤트는 아무런 영향을 미치지 않습니다.

복합 상태가 완료되었음을 감지하기

자식 상태는 최종 상태( QFinalState 객체)일 수 있으며, 최종 자식 상태가 입력되면 부모 상태는 QState::finished() 신호를 내보냅니다. 다음 다이어그램은 최종 상태로 들어가기 전에 몇 가지 처리를 수행하는 복합 상태 s1 를 보여줍니다:

s1 의 최종 상태가 입력되면 s1 은 자동으로 finished() 신호를 방출합니다. 이 이벤트가 상태 변경을 트리거하도록 하기 위해 신호 전환을 사용합니다:

  s1->addTransition(s1, &QState::finished, s2);

복합 상태에서 최종 상태를 사용하는 것은 복합 상태의 내부 세부 사항을 숨기고 싶을 때, 즉 외부 세계가 할 수 있는 유일한 일은 상태에 들어가서 상태가 작업을 완료했을 때 알림을 받는 것뿐이어야 할 때 유용합니다. 이는 복잡한(깊게 중첩된) 상태 머신을 구축할 때 매우 강력한 추상화 및 캡슐화 메커니즘입니다. (위의 예에서는 물론 s1finished() 신호에 의존하지 않고 s1done 상태에서 직접 전환을 만들 수 있지만, 그 결과 s1 의 구현 세부 사항이 노출되고 의존하게 됩니다.)

병렬 상태 그룹의 경우 모든 하위 상태가 최종 상태에 진입하면 QState::finished() 신호가 전송됩니다.

타깃 없는 전환

전환에는 목표 상태가 필요하지 않습니다. 타깃이 없는 전환은 다른 전환과 동일한 방식으로 트리거할 수 있지만, 타깃 없는 전환이 트리거될 때 상태 변경을 일으키지 않는다는 차이점이 있습니다. 이를 통해 머신이 특정 상태에 있을 때 해당 상태를 벗어나지 않고도 신호나 이벤트에 반응할 수 있습니다. 예시:

QStateMachine machine;
QState *s1 = new QState(&machine);

QPushButton button;
QSignalTransition *trans = new QSignalTransition(&button, &QPushButton::clicked);
s1->addTransition(trans);

QMessageBox msgBox;
msgBox.setText("The button was clicked; carry on.");
QObject::connect(trans, QSignalTransition::triggered, &msgBox, &QMessageBox::exec);

machine.setInitialState(s1);

버튼을 클릭할 때마다 메시지 상자가 표시되지만 상태 머신은 현재 상태(s1)로 유지됩니다. 그러나 대상 상태가 명시적으로 s1로 설정되어 있으면 매번 s1이 종료되었다가 다시 입력됩니다(예: QAbstractState::entered() 및 QAbstractState::exited() 신호가 방출됨).

이벤트, 트랜지션 및 가드

QStateMachine 는 자체 이벤트 루프를 실행합니다. 신호 전환(QSignalTransition 객체)의 경우 QStateMachine 은 해당 신호를 가로채면 자동으로 QStateMachine::SignalEvent 을 게시하고, 마찬가지로 QObject 이벤트 전환(QEventTransition 객체)의 경우 QStateMachine::WrappedEvent 을 게시합니다.

QStateMachine::postEvent()를 사용하여 자체 이벤트를 상태 머신에 게시할 수 있습니다.

사용자 지정 이벤트를 상태 머신에 게시할 때는 일반적으로 해당 유형의 이벤트에서 트리거할 수 있는 하나 이상의 사용자 지정 전환도 함께 게시합니다. 이러한 전환을 만들려면 QAbstractTransition 을 서브클래싱하고 eventTest()을 다시 구현하여 이벤트가 이벤트 유형(및 선택적으로 이벤트 객체의 속성 등 다른 기준)과 일치하는지 확인합니다.

여기서는 상태 머신에 문자열을 게시하기 위한 자체 사용자 정의 이벤트 유형인 StringEvent 을 정의합니다:

struct StringEvent : public QEvent
{
    StringEvent(const QString &val)
    : QEvent(QEvent::Type(QEvent::User+1)),
      value(val) {}

    QString value;
};

다음으로 이벤트의 문자열이 특정 문자열과 일치할 때만 트리거되는 전환을 정의합니다( 가드된 전환):

class StringTransition : public QAbstractTransition
{
    Q_OBJECT

public:
    StringTransition(const QString &value)
        : m_value(value) {}

protected:
    bool eventTest(QEvent *e) override
    {
        if (e->type() != QEvent::Type(QEvent::User+1)) // StringEvent
            return false;
        StringEvent *se = static_cast<StringEvent*>(e);
        return (m_value == se->value);
    }

    void onTransition(QEvent *) override {}

private:
    QString m_value;
};

eventTest() 재구현에서는 먼저 이벤트 유형이 원하는 유형인지 확인하고, 그렇다면 이벤트를 StringEvent 으로 캐스팅하고 문자열 비교를 수행합니다.

다음은 사용자 지정 이벤트와 전환을 사용하는 상태 차트입니다:

상태차트의 구현은 다음과 같습니다:

    QStateMachine machine;
    QState *s1 = new QState();
    QState *s2 = new QState();
    QFinalState *done = new QFinalState();

    StringTransition *t1 = new StringTransition("Hello");
    t1->setTargetState(s2);
    s1->addTransition(t1);
    StringTransition *t2 = new StringTransition("world");
    t2->setTargetState(done);
    s2->addTransition(t2);

    machine.addState(s1);
    machine.addState(s2);
    machine.addState(done);
    machine.setInitialState(s1);

머신이 시작되면 이벤트를 게시할 수 있습니다.

    machine.postEvent(new StringEvent("Hello"));
    machine.postEvent(new StringEvent("world"));

관련 트랜지션이 처리하지 않는 이벤트는 상태 머신에서 조용히 소비됩니다. 상태를 그룹화하고 이러한 이벤트의 기본 처리를 제공하는 것이 유용할 수 있습니다(예: 다음 상태 차트에서 볼 수 있듯이):

깊게 중첩된 상태 차트의 경우 가장 적절한 세부 수준에서 이러한 "대체" 전환을 추가할 수 있습니다.

복원 정책을 사용하여 자동으로 속성 복원하기

일부 상태 머신에서는 상태가 더 이상 활성화되지 않았을 때 복원하는 것이 아니라 상태의 속성을 할당하는 데 주의를 집중하는 것이 유용할 수 있습니다. 머신이 명시적으로 프로퍼티에 값을 지정하지 않은 상태에 들어갈 때 프로퍼티를 항상 초기 값으로 복원해야 한다는 것을 알고 있다면, 전역 복원 정책을 QStateMachine::RestoreProperties로 설정할 수 있습니다.

QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

이 복원 정책을 설정하면 머신이 모든 속성을 자동으로 복원합니다. 지정된 프로퍼티가 설정되지 않은 상태로 들어가면 먼저 상위 계층 구조를 검색하여 해당 프로퍼티가 정의되어 있는지 확인합니다. 정의되어 있으면 가장 가까운 조상에 정의된 값으로 프로퍼티가 복원됩니다. 그렇지 않은 경우 초기 값(즉, 상태의 속성 할당이 실행되기 전의 속성 값)으로 복원됩니다.

다음 코드를 살펴봅시다:

    QStateMachine machine;
    machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

    QState *s1 = new QState();
    s1->assignProperty(object, "fooBar", 1.0);
    machine.addState(s1);
    machine.setInitialState(s1);

    QState *s2 = new QState();
    machine.addState(s2);

컴퓨터가 시작될 때 fooBar 속성이 0.0이라고 가정해 보겠습니다. 머신이 상태 s1 에 있을 때 상태는 명시적으로 이 값을 할당하므로 속성은 1.0이 됩니다. 머신이 s2 상태일 때는 속성에 대해 명시적으로 정의된 값이 없으므로 암시적으로 0.0으로 복원됩니다.

중첩 상태를 사용하는 경우 부모가 프로퍼티에 대한 값을 정의하면 프로퍼티에 명시적으로 값을 할당하지 않는 모든 자손이 상속받습니다.

    QStateMachine machine;
    machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

    QState *s1 = new QState();
    s1->assignProperty(object, "fooBar", 1.0);
    machine.addState(s1);
    machine.setInitialState(s1);

    QState *s2 = new QState(s1);
    s2->assignProperty(object, "fooBar", 2.0);
    s1->setInitialState(s2);

    QState *s3 = new QState(s1);

여기서 s1 에는 s2s3 라는 두 개의 자식이 있습니다. s2 을 입력하면 fooBar 속성은 상태에 대해 명시적으로 정의되어 있으므로 2.0 값을 갖습니다. 머신이 상태 s3 인 경우 상태에 대한 값이 정의되어 있지 않지만 s1 은 속성을 1.0으로 정의하므로 fooBar 에 할당될 값이 이 값입니다.

애니메이션과 스테이트 머신

스테이트 머신 API는 애니메이션 프레임워크와 연결하여 프로퍼티가 스테이트에 할당될 때 자동으로 애니메이션을 적용할 수 있도록 합니다.

상태 머신은 애니메이션을 재생할 수 있는 특수 상태를 제공합니다. QState 는 상태가 입력되거나 종료될 때 프로퍼티를 설정할 수 있으며, 이 특수 애니메이션 상태는 QPropertyAnimation 이 주어질 때 이러한 값 사이에 보간됩니다.

QSignalTransition 또는 QEventTransition 클래스를 사용하여 하나 이상의 애니메이션을 상태 간 전환에 연결할 수 있습니다. 이러한 클래스는 모두 전환이 발생할 때 트리거되는 하나 이상의 애니메이션을 추가할 수 있는 편의 함수 addAnimation()를 정의하는 QAbstractTransition 에서 파생됩니다.

또한 시작 및 종료 값을 직접 설정하는 대신 프로퍼티를 상태와 연관시킬 수도 있습니다.

다음 코드가 있다고 가정해 보겠습니다:

    QState *s1 = new QState();
    QState *s2 = new QState();

    s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
    s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));

    s1->addTransition(button, &QPushButton::clicked, s2);

여기서는 사용자 인터페이스의 두 가지 상태를 정의합니다. s1 에서는 button 이 작고 s2 에서는 더 큽니다. s1 에서 s2 으로 전환하기 위해 버튼을 클릭하면 지정된 상태가 입력되면 버튼의 지오메트리가 즉시 설정됩니다. 그러나 전환을 부드럽게 하려면 QPropertyAnimation 을 만들어 전환 객체에 추가하기만 하면 됩니다.

    QState *s1 = new QState();
    QState *s2 = new QState();

    s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
    s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));

    QSignalTransition *transition = s1->addTransition(button, &QPushButton::clicked, s2);
    transition->addAnimation(new QPropertyAnimation(button, "geometry"));

해당 프로퍼티에 애니메이션을 추가하면 상태가 입력되었을 때 프로퍼티 할당이 더 이상 즉시 적용되지 않습니다. 대신 상태가 입력되면 애니메이션이 재생되기 시작하여 속성 할당을 부드럽게 애니메이션화합니다. 애니메이션의 시작 값이나 종료 값은 설정하지 않으므로 암시적으로 설정됩니다. 애니메이션의 시작 값은 애니메이션이 시작될 때 속성의 현재 값이 되고, 종료 값은 상태에 대해 정의된 속성 할당을 기반으로 설정됩니다.

스테이트 머신의 전역 복원 정책이 QStateMachine::RestoreProperties로 설정되어 있으면 프로퍼티 복원을 위한 애니메이션을 추가할 수도 있습니다.

모든 프로퍼티가 스테이트에 설정되었는지 감지하기

애니메이션을 사용하여 프로퍼티를 할당할 때 상태는 더 이상 머신이 주어진 상태에 있을 때 프로퍼티가 갖게 될 정확한 값을 정의하지 않습니다. 애니메이션이 실행되는 동안에는 애니메이션에 따라 프로퍼티가 어떤 값을 가질 수 있습니다.

어떤 경우에는 프로퍼티가 실제로 스테이트에 정의된 값을 할당받은 시점을 감지할 수 있으면 유용할 수 있습니다.

다음 코드가 있다고 가정해 보겠습니다:

    QMessageBox *messageBox = new QMessageBox(mainWindow);
    messageBox->addButton(QMessageBox::Ok);
    messageBox->setText("Button geometry has been set!");
    messageBox->setIcon(QMessageBox::Information);

    QState *s1 = new QState();

    QState *s2 = new QState();
    s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
    connect(s2, &QState::entered, messageBox, SLOT(exec()));

    s1->addTransition(button, &QPushButton::clicked, s2);

button 을 클릭하면 s2 상태로 전환되어 버튼의 지오메트리를 설정한 다음 사용자에게 지오메트리가 변경되었음을 알리는 메시지 상자를 표시합니다.

애니메이션을 사용하지 않는 일반적인 경우에는 예상대로 작동합니다. geometry 그러나 buttons1s2 사이의 전환에 애니메이션이 설정되어 있는 경우 s2 을 입력하면 애니메이션이 시작되지만 애니메이션 실행이 완료되기 전에 geometry 속성이 실제로 정의된 값에 도달하지 않습니다. 이 경우 버튼의 지오메트리가 실제로 설정되기 전에 메시지 상자가 팝업됩니다.

지오메트리가 실제로 최종 값에 도달할 때까지 메시지 상자가 팝업되지 않도록 하기 위해 상태의 propertiesAssigned() 신호를 사용할 수 있습니다. propertiesAssigned () 신호는 프로퍼티에 최종 값이 할당되면 즉시 또는 애니메이션 재생이 완료된 후에 발생하게 됩니다.

    QMessageBox *messageBox = new QMessageBox(mainWindow);
    messageBox->addButton(QMessageBox::Ok);
    messageBox->setText("Button geometry has been set!");
    messageBox->setIcon(QMessageBox::Information);

    QState *s1 = new QState();

    QState *s2 = new QState();
    s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));

    QState *s3 = new QState();
    connect(s3, &QState::entered, messageBox, SLOT(exec()));

    s1->addTransition(button, &QPushButton::clicked, s2);
    s2->addTransition(s2, &QState::propertiesAssigned, s3);

이 예제에서는 button 을 클릭하면 s2 상태가 됩니다. geometry 프로퍼티가 QRect(0, 0, 50, 50) 로 설정될 때까지 s2 상태로 유지됩니다. 그런 다음 s3 로 전환됩니다. s3 을 입력하면 메시지 상자가 팝업됩니다. s2 로의 전환에 geometry 속성에 대한 애니메이션이 있는 경우 애니메이션 재생이 완료될 때까지 컴퓨터는 s2 상태로 유지됩니다. 이러한 애니메이션이 없으면 프로퍼티를 설정하고 즉시 s3 상태로 전환됩니다.

어느 쪽이든 머신이 s3 상태에 있으면 geometry 속성에 정의된 값이 할당된 것으로 보장됩니다.

글로벌 복원 정책이 QStateMachine::RestoreProperties로 설정된 경우, 상태는 이 정책도 실행될 때까지 propertiesAssigned() 신호를 보내지 않습니다.

애니메이션이 완료되기 전에 스테이트가 종료되면 어떻게 되나요?

스테이트에 프로퍼티가 할당되어 있고 스테이트 전환에 프로퍼티에 대한 애니메이션이 있는 경우, 스테이트가 정의한 값에 프로퍼티가 할당되기 전에 스테이트가 종료될 수 있습니다. 특히 이전 섹션에서 설명한 것처럼 propertiesAssigned() 신호에 의존하지 않는 상태에서 전환이 있는 경우에 해당됩니다.

상태 머신 API는 상태 머신에 의해 할당된 속성도 보장합니다:

  • 프로퍼티에 명시적으로 할당된 값이 있습니다.
  • 현재 프로퍼티에 명시적으로 할당된 값으로 애니메이션이 적용되고 있습니다.

애니메이션이 완료되기 전에 상태가 종료되면 상태 머신의 동작은 전환의 대상 상태에 따라 달라집니다. 대상 상태가 프로퍼티에 명시적으로 값을 할당하는 경우 추가 작업이 수행되지 않습니다. 프로퍼티에는 대상 상태에 정의된 값이 할당됩니다.

대상 상태가 프로퍼티에 값을 할당하지 않는 경우에는 두 가지 옵션이 있습니다: 기본적으로 프로퍼티에는 떠나는 상태에 정의된 값(애니메이션 재생이 허용되었다면 할당되었을 값)이 할당됩니다. 그러나 전역 복원 정책이 설정되어 있으면 이 정책이 우선 적용되며 프로퍼티는 평소와 같이 복원됩니다.

기본 애니메이션

앞서 설명한 대로 전환에 애니메이션을 추가하여 대상 상태의 프로퍼티 할당에 애니메이션이 적용되도록 할 수 있습니다. 어떤 전환을 사용하든 특정 애니메이션을 특정 프로퍼티에 사용하려면 해당 애니메이션을 스테이트 머신에 기본 애니메이션으로 추가하면 됩니다. 이는 머신을 구성할 때 특정 상태에 의해 할당(또는 복원)된 속성을 알 수 없는 경우에 특히 유용합니다.

QState *s1 = new QState();
QState *s2 = new QState();

s2->assignProperty(object, "fooBar", 2.0);
s1->addTransition(s2);

QStateMachine machine;
machine.setInitialState(s1);
machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));

머신이 상태 s2 인 경우 이 속성은 s2 에 의해 할당되므로 fooBar 속성에 대한 기본 애니메이션이 머신에서 재생됩니다.

트랜지션에 명시적으로 설정된 애니메이션은 주어진 프로퍼티의 기본 애니메이션보다 우선한다는 점에 유의하세요.

중첩 상태 머신

QStateMachineQState 의 서브클래스이며, 이를 통해 스테이트 머신이 다른 머신의 하위 스테이트가 될 수 있습니다. QStateMachineQState::onEntry()를 재구현하고 QStateMachine::start()을 호출하여 하위 스테이트 머신이 입력되면 자동으로 실행이 시작되도록 합니다.

부모 상태 머신은 상태 머신 알고리즘에서 자식 상태 머신을 원자 상태로 취급합니다. 자식 상태 머신은 자체 이벤트 대기열과 구성을 유지하며 독립적입니다. 특히, 자식 머신의 configuration()는 부모 머신의 구성에 포함되지 않습니다(자식 머신 자체만 포함됨).

자식 상태 머신의 상태는 부모 상태 머신의 트랜지션 대상으로 지정할 수 없으며, 자식 상태 머신 자체만 지정할 수 있습니다. 반대로 부모 상태 머신의 상태는 자식 상태 머신의 트랜지션 대상으로 지정할 수 없습니다. 자식 상태 머신의 finished() 신호는 부모 상태 머신에서 트랜지션을 트리거하는 데 사용할 수 있습니다.

Qt State Machine 개요Qt State Machine QML 가이드도참조하세요 .

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