QtのImage.currentFrame徹底解説:アニメーション制御の基本

2025-05-31

いくつか異なる文脈でこの用語が出てくる可能性があるため、それぞれのケースで説明します。

QMLのAnimatedImageにおける currentFrame

QML (Qt Quick) では、GIFのようなアニメーション画像を扱うためにAnimatedImageという要素があります。このAnimatedImage要素には、現在表示されているフレームのインデックスを示す読み取り専用のプロパティとしてcurrentFrameが存在します。


  • import QtQuick 2.0
    
    Rectangle {
        width: 300
        height: 300
    
        AnimatedImage {
            id: myAnimation
            source: "animation.gif" // アニメーションGIFのパス
            anchors.centerIn: parent
        }
    
        Text {
            text: "Current Frame: " + myAnimation.currentFrame // 現在のフレーム番号を表示
            anchors.bottom: parent.bottom
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }
    
    この例では、myAnimation.currentFrameを使って、AnimatedImageが現在表示しているフレームの番号をText要素に表示しています。
  • 用途
    アニメーションの現在の再生位置を把握したり、それに基づいて他のQML要素を同期してアニメーションさせたりする際に利用します。

C++のQMovieにおける currentFrameNumber()

C++の世界では、QMovieクラスがアニメーション(GIFなど)の再生を扱います。QMovieには、currentFrameNumber()というメソッドがあり、これがQMLのcurrentFrameと同様に、現在表示されているフレームの番号を返します。また、currentImage()currentPixmap()を使って、そのフレームをQImageQPixmapとして取得することも可能です。


  • #include <QApplication>
    #include <QLabel>
    #include <QMovie>
    #include <QVBoxLayout>
    #include <QWidget>
    
    int main(int argc, char *argv[]) {
        QApplication a(argc, argv);
    
        QWidget window;
        QVBoxLayout *layout = new QVBoxLayout(&window);
    
        QLabel *label = new QLabel(&window);
        QMovie *movie = new QMovie("animation.gif"); // アニメーションGIFのパス
    
        if (movie->isValid()) {
            label->setMovie(movie);
            movie->start();
    
            // フレームが変更されるたびに現在のフレーム番号をコンソールに出力する例
            QObject::connect(movie, &QMovie::frameChanged, [&](int frameNumber) {
                qDebug() << "Current frame number: " << frameNumber;
                // ここで movie->currentImage() を使ってQImageを取得することも可能
            });
        } else {
            label->setText("アニメーションを読み込めませんでした。");
        }
    
        layout->addWidget(label);
        window.setLayout(layout);
        window.show();
    
        return a.exec();
    }
    
  • 用途
    プログラムでアニメーションを制御し、特定のフレームの画像データにアクセスする際に使用します。

QImageReaderクラスは、画像ファイル(特にアニメーションを含むもの)を読み込むための低レベルなインターフェースを提供します。アニメーションに対応する画像形式の場合、QImageReader::currentImageNumber()メソッドを使って、現在読み込み中のフレームのシーケンス番号を取得できます。

  • 注意
    QMovieがアニメーションの再生を「管理」するのに対し、QImageReaderはファイルの「読み込み」に特化しています。
  • 用途
    アニメーション画像ファイルをフレームごとに手動で読み込む際に、どのフレームを処理しているかを確認するために使用します。


アニメーションが再生されない、currentFrameが常に0または予期せぬ値になる

原因

  • QML AnimatedImageのplayingプロパティがfalse
    明示的にplaying: falseと設定されている、または何らかの理由でfalseになっている。
  • アニメーションの開始忘れ (C++ QMovie)
    QMovieを使用している場合、start()メソッドを呼び出していない。
  • アニメーションではない画像
    単一の静止画像をアニメーション画像として扱おうとしている。
  • サポートされていない形式
    Qtがその画像形式(特にアニメーション)をサポートしていない。必要なイメージフォーマットプラグインが不足している可能性がある。
  • 画像ファイルの読み込み失敗
    指定されたアニメーション画像(GIFなど)が存在しない、パスが間違っている、または破損している。

トラブルシューティング

  • AnimatedImage::playingの確認 (QML)
    QMLのAnimatedImageの場合、playing: trueが設定されているか確認します。

    AnimatedImage {
        source: "animation.gif"
        playing: true // これがfalseだと再生されない
        // ...
    }
    
  • QMovie::start()の呼び出し (C++)
    QMovieオブジェクトを作成したら、必ずmovie->start()を呼び出してアニメーションを開始します。

  • QMovie::isValid()の確認 (C++)
    QMovieを使用する前にmovie->isValid()で画像が正常に読み込まれたか確認します。

  • 静止画ではないことの確認
    アニメーションとして扱いたい画像が実際に複数のフレームを持つアニメーションファイルであることを確認します。

  • 必要なプラグインの確認
    Qtのインストールに、使用する画像形式に対応するイメージフォーマットプラグイン(例: qgif.dlllibqgif.soなど)が含まれているか確認します。これらのプラグインは通常、Qtのインストールディレクトリ内のplugins/imageformats以下にあります。

  • 画像形式の確認
    使用している画像形式がQtでサポートされているか確認します。特にGIFは通常サポートされていますが、特殊なエンコードや破損がある場合は読み込めないことがあります。

  • ファイルパスの確認
    画像ファイルのパスが正しいことを確認します。絶対パス、相対パスのどちらを使用しているか、特にQMLの場合、QMLファイルからの相対パスに注意してください。

currentFrameの更新が遅い、または反応しない

原因

  • フレーム変更シグナルの購読忘れ (C++ QMovie)
    QMovieの場合、frameChangedシグナルに接続していない場合、フレーム変更をイベントで受け取ることはできません。
  • UIスレッドのブロック
    QML/C++のコード内で重い処理がUIスレッドをブロックしている場合、アニメーションの更新も停滞し、currentFrameの反映が遅れることがあります。
  • 低速なアニメーション
    アニメーション自体のフレームレートが非常に低い場合、currentFrameの更新もゆっくりになります。

トラブルシューティング

  • QML onCurrentFrameChangedシグナルハンドラの利用
    QMLのAnimatedImageではonCurrentFrameChangedシグナルハンドラが利用できます。

    AnimatedImage {
        id: myAnimation
        source: "animation.gif"
        onCurrentFrameChanged: {
            console.log("Current Frame: " + myAnimation.currentFrame)
        }
    }
    
  • QMovie::frameChangedシグナルの利用 (C++)
    QMoviecurrentFrameNumber()を定期的にポーリングするのではなく、frameChangedシグナルに接続して、フレームが実際に変更されたときにのみ処理を行うようにします。

    // QMovieを例に
    QObject::connect(movie, &QMovie::frameChanged, [&](int frameNumber) {
        // フレームが変更されたときにのみ実行される処理
        qDebug() << "Frame changed to: " << frameNumber;
    });
    
  • 重い処理のオフロード
    currentFrameの値に依存する処理が重い場合は、非同期処理(WorkerScriptやC++のスレッド)にオフロードすることを検討します。UIスレッドはスムーズな描画とイベント処理のために常に空けておくべきです。

  • アニメーションの速度を確認
    別の画像ビューアなどでアニメーションファイル自体の再生速度を確認します。

currentFrameの値が範囲外、または予期せぬジャンプをする

原因

  • リソースの再ロード
    sourceプロパティが変更されるなどしてAnimatedImageが再ロードされた場合、currentFrameはリセットされる可能性があります。
  • ループの開始/終了
    アニメーションがループする設定の場合、ループの境界でcurrentFrameが0に戻ったり、最後にジャンプしたりするのは正常な動作です。
  • アニメーションファイルの異常
    アニメーションファイル自体が破損している、または非標準的な形式である場合、フレームカウントが正確に取得できないことがあります。

トラブルシューティング

  • sourceプロパティの変更
    AnimatedImagesourceが動的に変更される場合、currentFrameがリセットされることを想定し、その後のロジックを適切に記述します。
  • ループ動作の理解
    アニメーションがループするように設定されているか(AnimatedImage::loopsQMovie::setLoopCount())を確認し、その動作を考慮に入れます。
  • アニメーションファイルの健全性確認
    アニメーションファイルを別のツールで開き、正常に再生されるか、フレーム数に異常がないか確認します。

currentFrameを利用したQMLアニメーションや同期がずれる

原因

  • QMLバインディングの遅延
    QMLのバインディングは非同期で実行されることがあるため、currentFrameの変更が他のプロパティにすぐに反映されない場合がある。
  • フレームレートの不一致
    AnimatedImageの実際のフレームレートと、同期させたい他のアニメーションのフレームレートが異なる。
  • 同期タイミングのずれ
    currentFrameが更新されるタイミングと、それに基づいて他の要素をアニメーションさせるタイミングに微妙なずれがある。

トラブルシューティング

  • 明示的なupdate()/repaint()の回避
    QMLでは通常、プロパティの変更は自動的にUIを更新します。手動でupdate()repaint()を呼び出す必要はほとんどありません。これらを不適切に使用すると、かえってパフォーマンスの問題や描画のずれを引き起こす可能性があります。
  • Animation要素との連携
    より複雑な同期が必要な場合、QMLのAnimation要素(NumberAnimationPropertyAnimationなど)を使用して、currentFrameの値に基づいて他のプロパティをアニメーションさせることを検討します。currentFrameの値をtargetとして利用することで、より密接な同期が可能です。
  • onCurrentFrameChangedシグナルハンドラの活用
    currentFrameの値が変更されたときにのみ、同期させたい他の要素のプロパティを更新するようにします。これにより、変更イベントに基づいた同期がより確実になります。
  • Qtのバージョンとドキュメント
    使用しているQtのバージョンが古い場合、既知のバグが存在する可能性があります。最新のQtバージョンで試すか、該当バージョンのドキュメントを確認して、プロパティの動作や既知の制限事項を把握します。
  • シンプルなテストケース
    問題が複雑な場合、問題の核心部分だけを切り出したシンプルなQMLファイルやC++プログラムを作成し、そこで動作を確認します。
  • Qt CreatorのQMLデバッガー
    QMLデバッガーを使用して、AnimatedImageのプロパティ(currentFrameplayingsourceなど)の値をリアルタイムで監視します。
  • console.log() / qDebug() の活用
    currentFrameの値がどのように変化しているか、実際にログに出力して確認します。


以下に、それぞれのプログラミング言語での使用例を示します。

QML (Qt Quick) の例

この例では、AnimatedImageを使ってGIFアニメーションを表示し、currentFrameプロパティをText要素にバインドして現在のフレーム番号を表示します。また、アニメーションの再生/一時停止を制御するボタンも追加します。

ファイル構成

  • animation.gif (任意のGIFアニメーションファイル)
  • main.qml (QMLコード)

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 400
    height: 400
    visible: true
    title: "AnimatedImage currentFrame Example"

    Column {
        anchors.centerIn: parent
        spacing: 10

        AnimatedImage {
            id: gifAnimation
            source: "animation.gif" // プロジェクト内のGIFアニメーションのパス
            width: 200
            height: 200
            anchors.horizontalCenter: parent.horizontalCenter
            playing: true // デフォルトで再生を開始

            // currentFrameが変更されたときにコンソールに出力
            onCurrentFrameChanged: {
                console.log("Current Frame: " + currentFrame + " / Total Frames: " + frameCount);
            }
        }

        Text {
            id: frameDisplay
            text: "現在のフレーム: " + gifAnimation.currentFrame + " / " + gifAnimation.frameCount
            font.pointSize: 16
            horizontalAlignment: Text.AlignHCenter
            width: parent.width
        }

        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10

            Button {
                text: "再生 / 一時停止"
                onClicked: {
                    gifAnimation.playing = !gifAnimation.playing;
                }
            }

            Button {
                text: "最初のフレームに戻る"
                onClicked: {
                    gifAnimation.currentFrame = 0; // 最初のフレームに設定
                    // 0番目のフレームで停止させる場合は playing = false; も必要
                }
            }
        }
    }
}

解説

  • ボタンを使ってplayingプロパティを切り替えたり、currentFrameを直接設定して特定のフレームにジャンプしたりできます。
  • onCurrentFrameChangedシグナルハンドラは、currentFrameの値が変更されるたびにトリガーされ、コンソールにログを出力します。
  • Text要素でgifAnimation.currentFramegifAnimation.frameCountをバインドすることで、現在のフレームと総フレーム数を表示できます。
  • frameCountプロパティは、アニメーションの総フレーム数を取得します。
  • currentFrameプロパティは、現在表示されているフレームのインデックスを自動的に更新します。
  • playing: trueでアニメーションの再生を開始します。
  • AnimatedImage要素のsourceプロパティでGIFアニメーションを指定します。

C++では、QMovieクラスを使用してアニメーションを扱い、currentFrameNumber()メソッドで現在のフレーム番号を取得します。QLabelQMovieを設定することで、アニメーションを表示できます。

ファイル構成

  • animation.gif (任意のGIFアニメーションファイル)
  • main.cpp (C++コード)

main.cpp

#include <QApplication>
#include <QLabel>
#include <QMovie>
#include <QVBoxLayout>
#include <QPushButton>
#include <QHBoxLayout>
#include <QWidget>
#include <QDebug> // for qDebug()

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    QWidget window;
    QVBoxLayout *mainLayout = new QVBoxLayout(&window);

    QLabel *animationLabel = new QLabel(&window);
    QMovie *movie = new QMovie("animation.gif"); // プロジェクト内のGIFアニメーションのパス

    if (movie->isValid()) {
        animationLabel->setMovie(movie);
        movie->start(); // アニメーションを開始

        // frameChangedシグナルを接続して、フレームが変更されるたびに情報を表示
        QObject::connect(movie, &QMovie::frameChanged, [&](int frameNumber) {
            qDebug() << "Current C++ Frame Number: " << frameNumber
                     << " / Total Frames: " << movie->frameCount();
            // QLabelのテキストを更新することも可能
            // animationLabel->setText(QString("Current Frame: %1 / %2").arg(frameNumber).arg(movie->frameCount()));
        });
    } else {
        animationLabel->setText("アニメーションファイルを読み込めませんでした。");
    }

    mainLayout->addWidget(animationLabel);

    QHBoxLayout *buttonLayout = new QHBoxLayout();
    QPushButton *playPauseButton = new QPushButton("再生 / 一時停止");
    QPushButton *resetButton = new QPushButton("最初のフレームに戻る");

    QObject::connect(playPauseButton, &QPushButton::clicked, [&]() {
        if (movie->state() == QMovie::Running) {
            movie->setPaused(true);
            playPauseButton->setText("再生");
        } else {
            movie->setPaused(false);
            playPauseButton->setText("一時停止");
        }
    });

    QObject::connect(resetButton, &QPushButton::clicked, [&]() {
        movie->jumpToFrame(0); // 最初のフレームにジャンプ
        movie->setPaused(false); // 再生を再開
        playPauseButton->setText("一時停止");
    });

    buttonLayout->addWidget(playPauseButton);
    buttonLayout->addWidget(resetButton);
    mainLayout->addLayout(buttonLayout);

    window.setLayout(mainLayout);
    window.resize(300, 300);
    window.show();

    return a.exec();
}
  • ボタンのクリックイベントを接続し、movie->setPaused()で再生/一時停止を切り替えたり、movie->jumpToFrame(0)で特定のフレームにジャンプしたりできます。
  • movie->frameCount()で総フレーム数を取得できます。
  • QObject::connectを使用して、QMovieframeChanged(int)シグナルをラムダ関数に接続します。これにより、フレームが変更されるたびに現在のフレーム番号がframeNumber引数として渡され、qDebug()で出力されます。
  • animationLabel->setMovie(movie)QLabelQMovieを設定し、movie->start()で再生を開始します。
  • movie->isValid()でファイルが正しく読み込まれたか確認します。
  • QMovieオブジェクトを作成し、アニメーションファイルのパスを渡します。


主な代替方法をいくつか説明します。

タイマーベースの制御 (Timer-based Control)

アニメーションの各フレームが一定の時間間隔で切り替わる性質を利用し、明示的にタイマーを管理してアニメーションを制御する方法です。

QMLの例

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 400
    height: 400
    visible: true
    title: "Timer-based Animation Control"

    property int currentManualFrame: 0
    property int totalFrames: 10 // アニメーションの総フレーム数を事前に知っている場合

    // ダミーのアニメーション画像(ここではframe_0.png, frame_1.png...のような連番画像を想定)
    Image {
        id: animatedImage
        source: "images/frame_" + currentManualFrame + ".png" // 画像ファイルは別途用意
        width: 200
        height: 200
        anchors.centerIn: parent
    }

    Timer {
        id: frameTimer
        interval: 100 // 100msごとにフレームを更新(10FPS)
        repeat: true
        running: true // デフォルトで開始

        onTriggered: {
            currentManualFrame = (currentManualFrame + 1) % totalFrames;
        }
    }

    // 表示用のテキスト
    Text {
        text: "手動フレーム: " + currentManualFrame + " / " + totalFrames
        anchors.top: animatedImage.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        font.pointSize: 16
    }

    // 再生/一時停止ボタン
    Button {
        text: frameTimer.running ? "一時停止" : "再生"
        anchors.top: frameDisplay.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            frameTimer.running = !frameTimer.running
        }
    }
}

C++の例

#include <QApplication>
#include <QLabel>
#include <QTimer>
#include <QPixmap>
#include <QVBoxLayout>
#include <QPushButton>
#include <QHBoxLayout>
#include <QWidget>
#include <QDebug>

class ManualAnimator : public QWidget {
    Q_OBJECT
public:
    ManualAnimator(QWidget *parent = nullptr) : QWidget(parent), currentFrame(0), totalFrames(10) {
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        animationLabel = new QLabel(this);
        mainLayout->addWidget(animationLabel);

        QHBoxLayout *buttonLayout = new QHBoxLayout();
        QPushButton *playPauseButton = new QPushButton("再生 / 一時停止", this);
        QPushButton *resetButton = new QPushButton("最初のフレームに戻る", this);
        buttonLayout->addWidget(playPauseButton);
        buttonLayout->addWidget(resetButton);
        mainLayout->addLayout(buttonLayout);

        frameTimer = new QTimer(this);
        frameTimer->setInterval(100); // 100msごとに更新
        connect(frameTimer, &QTimer::timeout, this, &ManualAnimator::updateFrame);
        
        // 初期フレームの表示
        updateImage();

        connect(playPauseButton, &QPushButton::clicked, this, [this, playPauseButton]() {
            if (frameTimer->isActive()) {
                frameTimer->stop();
                playPauseButton->setText("再生");
            } else {
                frameTimer->start();
                playPauseButton->setText("一時停止");
            }
        });

        connect(resetButton, &QPushButton::clicked, this, [this]() {
            currentFrame = 0;
            updateImage();
        });

        frameTimer->start(); // アニメーションを開始
    }

private slots:
    void updateFrame() {
        currentFrame = (currentFrame + 1) % totalFrames;
        updateImage();
        qDebug() << "Current C++ Manual Frame: " << currentFrame;
    }

    void updateImage() {
        // 例: "images/frame_0.png", "images/frame_1.png" のように画像を読み込む
        // 実際のアプリケーションでは、QVector<QPixmap>などに事前に画像をロードしておくのが効率的
        QString imagePath = QString("images/frame_%1.png").arg(currentFrame);
        QPixmap pixmap(imagePath);
        if (!pixmap.isNull()) {
            animationLabel->setPixmap(pixmap.scaled(200, 200, Qt::KeepAspectRatio));
        } else {
            animationLabel->setText(QString("画像が見つかりません: %1").arg(imagePath));
        }
    }

private:
    QLabel *animationLabel;
    QTimer *frameTimer;
    int currentFrame;
    int totalFrames;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    ManualAnimator animator;
    animator.setWindowTitle("Timer-based Animation Control (C++)");
    animator.resize(300, 350);
    animator.show();
    return a.exec();
}
#include "main.moc" // MOCで生成されるファイルのインクルード

利点

  • UI以外のロジック(例えば、ゲームのロジック)とアニメーションの同期を取りやすいです。
  • GIFなどのアニメーションファイルではなく、個別の画像シーケンスを使用する場合に非常に有効です。
  • アニメーションの速度と進行を完全にプログラムで制御できます。

欠点

  • 画像ファイルを事前にロードしない場合、フレームごとにI/Oが発生しパフォーマンスが悪化する可能性があります。
  • 複雑なアニメーション(可変フレームレートなど)の再現は難しい場合があります。
  • アニメーションのフレーム数や、各フレームの表示時間を自分で管理する必要があります。

QMLのPropertyAnimation / C++のQVariantAnimation (アニメーションプロパティの制御)

直接フレーム番号を操作するのではなく、別のプロパティ(例: 0から1の範囲で進行するprogressプロパティ)をアニメーションさせ、そのprogress値に基づいて、表示する画像や要素の状態を決定する方法です。特に、アニメーションの補間やイージングが必要な場合に強力です。

QMLの例

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 400
    height: 400
    visible: true
    title: "PropertyAnimation for Frames"

    property real animationProgress: 0.0 // 0.0から1.0まで変化するプログレス
    property int totalFrames: 10

    Image {
        id: animatedImage
        // progressに基づいてフレームを計算
        source: "images/frame_" + Math.floor(animationProgress * totalFrames) % totalFrames + ".png"
        width: 200
        height: 200
        anchors.centerIn: parent
    }

    PropertyAnimation {
        id: frameProgressAnimation
        target: parent // Windowがターゲット
        property: "animationProgress"
        from: 0.0
        to: 1.0
        duration: 1000 // 1秒でアニメーション完了
        loops: Animation.Infinite // 無限ループ
        running: true
    }

    Text {
        text: "プログレス: " + animationProgress.toFixed(2) + " (フレーム: " + (Math.floor(animationProgress * totalFrames) % totalFrames) + ")"
        anchors.top: animatedImage.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        font.pointSize: 16
    }

    Button {
        text: frameProgressAnimation.running ? "一時停止" : "再生"
        anchors.top: frameDisplay.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            frameProgressAnimation.running = !frameProgressAnimation.running
        }
    }
}

利点

  • 複数のプロパティを同時にアニメーションさせることができます。
  • アニメーションの期間(duration)を指定するだけで、フレーム間の遷移が自動的に処理されます。
  • イージングカーブ(easing.type)を適用して、アニメーションの緩急を制御できます。

欠点

  • アニメーションの総フレーム数や各フレームの表示時間を明確に制御したい場合は、このアプローチが回りくどいことがあります。
  • 各フレームが個別の画像ファイルである場合に、フレーム番号の計算(Math.floor(progress * totalFrames))が必要になります。

State/Transition (QML) を使用したシーケンスアニメーション

QMLのStateTransitionを使用して、UI要素の状態変化に伴ってアニメーションを定義する方法です。直接フレーム番号を扱うわけではありませんが、一連の視覚的な変化を定義するのに適しています。

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 400
    height: 400
    visible: true
    title: "State/Transition Animation"

    // 複数の状態を定義
    states: [
        State {
            name: "frame1"
            PropertyChanges { target: myImage; source: "images/frame_0.png" }
        },
        State {
            name: "frame2"
            PropertyChanges { target: myImage; source: "images/frame_1.png" }
        },
        State {
            name: "frame3"
            PropertyChanges { target: myImage; source: "images/frame_2.png" }
        }
        // ... 他のフレームの状態を定義
    ]

    // 状態間の遷移を定義
    transitions: [
        Transition {
            from: "*" // どの状態からでも
            to: "frame2" // frame2へ
            SequentialAnimation {
                PauseAnimation { duration: 500 } // 0.5秒待機
            }
        },
        Transition {
            from: "frame2"
            to: "frame3"
            SequentialAnimation {
                PauseAnimation { duration: 500 }
            }
        },
        Transition {
            from: "frame3"
            to: "frame1" // ループ
            SequentialAnimation {
                PauseAnimation { duration: 500 }
            }
        }
    ]

    Image {
        id: myImage
        source: "images/frame_0.png" // 初期画像
        width: 200
        height: 200
        anchors.centerIn: parent
    }

    Component.onCompleted: {
        // 状態を順次切り替えてアニメーションを開始
        myImage.state = "frame2";
        // または、Timerを使って状態を制御
        var currentStateIndex = 0;
        var statesList = ["frame1", "frame2", "frame3"]; // 全ての状態名
        var timer = Qt.createTimer(500, true);
        timer.triggered.connect(function() {
            currentStateIndex = (currentStateIndex + 1) % statesList.length;
            myImage.state = statesList[currentStateIndex];
        });
    }
}

利点

  • 複雑なUIアニメーションシーケンスに適しています。
  • UI要素の状態変化とアニメーションを宣言的に定義できます。

欠点

  • フレーム番号を直接追跡するのには適していません。
  • 大量のフレームを持つアニメーションには向いていません(各フレームごとにStateを定義する必要があるため)。

QMovieAnimatedImageが提供する高レベルなアニメーション管理機能を使わず、QImageReaderを用いてアニメーションファイルからフレームを一つずつ手動で読み込む方法です。特定のフレームだけを抽出し処理したい場合などに使用します。

#include <QApplication>
#include <QLabel>
#include <QImageReader>
#include <QTimer>
#include <QVBoxLayout>
#include <QPushButton>
#include <QHBoxLayout>
#include <QWidget>
#include <QDebug>

class ImageReaderAnimator : public QWidget {
    Q_OBJECT
public:
    ImageReaderAnimator(QWidget *parent = nullptr) : QWidget(parent) {
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        animationLabel = new QLabel(this);
        mainLayout->addWidget(animationLabel);

        QHBoxLayout *buttonLayout = new QHBoxLayout();
        QPushButton *playPauseButton = new QPushButton("再生 / 一時停止", this);
        QPushButton *nextFrameButton = new QPushButton("次のフレーム", this);
        buttonLayout->addWidget(playPauseButton);
        buttonLayout->addWidget(nextFrameButton);
        mainLayout->addLayout(buttonLayout);

        reader = new QImageReader("animation.gif"); // アニメーションGIFのパス
        if (!reader->canRead()) {
            animationLabel->setText("アニメーションファイルを読み込めません: " + reader->errorString());
            return;
        }

        frameTimer = new QTimer(this);
        frameTimer->setInterval(100); // 100msごとに更新
        connect(frameTimer, &QTimer::timeout, this, &ImageReaderAnimator::displayNextFrame);

        connect(playPauseButton, &QPushButton::clicked, this, [this, playPauseButton]() {
            if (frameTimer->isActive()) {
                frameTimer->stop();
                playPauseButton->setText("再生");
            } else {
                frameTimer->start();
                playPauseButton->setText("一時停止");
            }
        });

        connect(nextFrameButton, &QPushButton::clicked, this, &ImageReaderAnimator::displayNextFrame);

        displayNextFrame(); // 最初のフレームを表示
        frameTimer->start(); // アニメーションを開始
    }

private slots:
    void displayNextFrame() {
        if (!reader->jumpToNextImage()) { // 次のフレームに移動
            reader->jumpToImage(0); // 最後のフレームに到達したら最初のフレームに戻る
            // または、ここでアニメーションを停止することも可能
            // frameTimer->stop();
        }
        QImage image = reader->read();
        if (!image.isNull()) {
            animationLabel->setPixmap(QPixmap::fromImage(image).scaled(200, 200, Qt::KeepAspectRatio));
            qDebug() << "Current C++ QImageReader Frame: " << reader->currentImageNumber();
        }
    }

private:
    QLabel *animationLabel;
    QImageReader *reader;
    QTimer *frameTimer;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    ImageReaderAnimator animator;
    animator.setWindowTitle("QImageReader Animation (C++)");
    animator.resize(300, 350);
    animator.show();
    return a.exec();
}
#include "main.moc" // MOCで生成されるファイルのインクルード

利点

  • QMovieがサポートしていない特殊なアニメーション形式を扱う際に、自分でパーサーを実装する出発点となります。
  • フレームごとにカスタム処理(画像加工など)を行うのに適しています。
  • アニメーションのフレームデータを低レベルで完全に制御できます。
  • パフォーマンスの最適化(画像のキャッシュなど)を自分で考慮する必要があります。
  • アニメーションの表示・再生ロジックをすべて手動で実装する必要があります。