ブロックの移動
「ブロックの移動」のサンプルでは、QStateMachine とカスタムトランジションを使用して、QGraphicsScene 内のアイテムをアニメーション化する方法を紹介しています。

このサンプルでは、上の画像に表示されている青いブロックにアニメーションを適用しています。このアニメーションにより、ブロックは4つのあらかじめ設定された位置の間を移動します。
このサンプルは、以下のクラスで構成されています。
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つしか含めていません。このグラフを構築するコードを1行ずつ検証し、グラフがどのように機能するかを説明します。まず、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.