Item.state

2025-06-06

QtプログラミングにおけるItem.stateは、主にQt Quick (QML) やGraphics View Frameworkにおいて、グラフィカルなアイテム(要素)の現在の状態を表す概念です。これにより、アイテムの見た目や振る舞いを、特定の状況に応じて変化させることができます。

具体的には、以下のような文脈で使われます。

QMLにおけるState (状態)

QMLでは、StateはUI要素の見た目やプロパティの集合を定義するために使われます。例えば、ボタンが「押された状態」「ホバー状態」「通常状態」といった異なる状態を持つことができます。

  • Transition (状態遷移): 状態が切り替わる際に、プロパティの変化をアニメーションさせるためにTransitionを使用できます。これにより、スムーズなUIの動きを実現します。
  • 切り替え: プログラムのロジックやユーザーインタラクション(マウスのクリック、キーボード入力など)に応じて、ある状態から別の状態へ切り替えることができます。
  • 定義: Stateは、Itemやそのサブクラス内で定義され、特定のプロパティ値のセットや、他の要素の可視性などを指定します。


Rectangle {
    id: myButton
    width: 100
    height: 50
    color: "lightgray"

    Text {
        anchors.centerIn: parent
        text: "Click Me"
    }

    // 状態の定義
    states: [
        State {
            name: "pressed" // 押された状態
            when: myButton.pressed // pressedプロパティがtrueのとき
            PropertyChanges { target: myButton; color: "darkgray"; scale: 0.95 }
        },
        State {
            name: "hovered" // ホバー状態
            when: myButton.hovered // hoveredプロパティがtrueのとき
            PropertyChanges { target: myButton; color: "lightblue" }
        }
    ]

    // 状態間の遷移アニメーション
    transitions: Transition {
        from: "*" // どの状態からでも
        to: "*" // どの状態へでも
        PropertyAnimation { properties: "color,scale"; duration: 100 }
    }
}

この例では、myButtonというRectangleが、マウスが押されたとき(pressed状態)と、マウスカーソルが上にあるとき(hovered状態)で色と大きさが変化します。

QtのGraphics View Frameworkでは、QGraphicsItemクラスがグラフィカルなアイテムの基底クラスとなります。この文脈でのItem.stateは、主にアイテムが持っている特定のフラグやプロパティによって表現される、アイテムの現在の属性を指すことが多いです。

例えば、QGraphicsItem::GraphicsItemFlagという列挙型で定義されるフラグを使って、アイテムのさまざまな状態を設定できます。

  • ItemSendsGeometryChanges: アイテムのジオメトリが変更されたときに通知を送信するかどうか。
  • ItemIsFocusable: アイテムがキーボードフォーカスを受け取れるかどうか。
  • ItemIsSelectable: アイテムが選択可能かどうか。
  • ItemIsMovable: アイテムがマウスで移動可能かどうか。

これらのフラグは、setFlags()関数で設定し、flags()関数で取得できます。これにより、アイテムが特定のユーザーインタラクションにどのように反応するかを制御できます。


#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QGraphicsScene>

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

    QGraphicsScene scene;
    QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 100, 100);
    rectItem->setBrush(Qt::blue);
    // アイテムを選択可能で、移動可能にする
    rectItem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    scene.addItem(rectItem);

    QGraphicsView view(&scene);
    view.show();

    return app.exec();
}

このC++の例では、QGraphicsRectItemに対してItemIsSelectableItemIsMovableのフラグを設定しています。これにより、ユーザーは長方形をマウスで選択したり、ドラッグして移動させたりできるようになります。



QMLにおける State の一般的なエラーとトラブルシューティング

QMLのStateは、UIの状態管理の中心となるため、関連するエラーも多岐にわたります。

状態が切り替わらない/期待通りにアニメーションしない

  • トラブルシューティング:
    • console.log() を使って、when 条件で使用しているプロパティの値が期待通りに変化しているか確認する。
    • Qt CreatorのQMLデバッガを使用して、実行時のアイテムのプロパティ値や現在の状態を確認する。
    • Transitionfromtoプロパティを確認し、意図した状態間の遷移がカバーされているか確認する。from: "*"to: "*" はすべての遷移に適用されますが、特定の遷移に特化したTransitionがある場合は、そちらが優先されます。
    • PropertyChangesブロック内のプロパティ名が、対象アイテムのプロパティと一致しているか確認する。
    • プロパティが変更される他の場所がないか、コード全体を確認する。
  • 原因:
    • when 条件が正しくない、または状態を切り替えるトリガーが不足している。
    • states リストに状態が定義されていない。
    • Transition が定義されていない、またはプロパティのアニメーションが正しく設定されていない。
    • 複数のStatewhen条件が同時にtrueになり、どの状態が適用されるべきか曖昧になっている(QMLはリストの最初の一致する状態を適用します)。
    • プロパティ変更がPropertyChangesの外で行われている、または他のバインディングと競合している。

プロパティのバインディング競合

  • トラブルシューティング:
    • QMLはプロパティの変更順序に依存することがあります。どの場所でプロパティが最後に設定されているかを確認し、意図した動作になるように調整する。
    • console.log() でプロパティの変更を追跡し、いつ、どこで値が変更されているかを把握する。
    • Qt CreatorのQMLデバッガでプロパティインスペクタを使用し、リアルタイムでのプロパティ値の変化を監視する。
  • 原因:
    • State内でプロパティを変更しているにも関わらず、同じプロパティがItemのルートレベルや別のState、またはJavaScriptコードでバインディングされている場合、競合が発生することがあります。

StateのスコープとIDの参照問題

  • トラブルシューティング:
    • targetプロパティに指定するIDが正しいか、そしてそのIDが現在のQMLファイルのスコープ内でアクセス可能かを確認する。
    • QMLのIDはファイル内でユニークである必要があります。重複するIDがないか確認する。
    • 親アイテムのプロパティを参照する場合は、parent.propertyName のように明示的にparentを使用する。
  • 原因:
    • State内で定義されたPropertyChangesAnchorChangesなどが、スコープ外のIDを参照しようとしている。
    • ネストされたItem内で親のItemの状態を変更しようとしているが、参照が適切でない。

パフォーマンスの問題(特に複雑なUIの場合)

  • トラブルシューティング:
    • Qt CreatorのQMLプロファイラを使用して、パフォーマンスのボトルネックを特定する。CPU使用率やフレームレートを監視する。
    • 不必要な状態遷移やアニメーションを減らす。
    • Transitionでアニメーションさせるプロパティを限定する(必要なものだけにする)。
    • LayerShaderEffectSourceなどを適切に使用し、再描画の範囲を最適化する。
    • LoaderStackViewなどを活用して、必要な時にだけUI要素をロード・アンロードする。
  • 原因:
    • 多数のStateや複雑なTransitionが、頻繁に状態変更を引き起こすことで、UIの応答性が低下することがあります。
    • Transition内のアニメーションが重い(例: ピクセル単位の複雑なシェーダーエフェクトなど)。
    • 状態変更が、必要以上に多くの要素の再描画をトリガーしている。

State内のPropertyChangesが適用されない

  • トラブルシューティング:
    • PropertyChangesが、対象のアイテムに正しく関連付けられているか確認する。多くの場合、Stateの親であるアイテムのプロパティを変更するか、targetプロパティを使って明示的に変更対象を指定します。
  • 原因:
    • PropertyChangesが、そのStateが適用されるItemの直接の子ではないItemのプロパティを変更しようとしている。PropertyChangesは、その親のアイテム、またはtargetプロパティで明示的に指定されたアイテムのプロパティを変更します。
    • QMLのバージョンによっては、Stateの適用ルールが微妙に異なる場合がある(稀ですが)。

AnchorChangesの不適切な使用

  • トラブルシューティング:
    • AnchorChangesPropertyChangesを組み合わせて、アンカーとサイズの変更を同期させる。
    • アンカーのバインディングが論理的であり、無限ループを引き起こさないか確認する。
  • 原因:
    • AnchorChangesはアンカーの変更を状態遷移させるために使いますが、これだけではアンカーの変更が適用されないことがあります。PropertyChangeswidthheightなどのサイズプロパティも変更する必要がある場合があります。
    • 循環参照を伴うアンカーの設定。

QGraphicsItemにおける ItemState (フラグ) の一般的なエラーとトラブルシューティング

QGraphicsItemItemStateは、QMLのStateとは異なり、アイテムの属性や挙動を制御するフラグのセットです。ここでは主にC++での利用を想定します。

アイテムが移動できない/選択できない/フォーカスを受け取れない

  • トラブルシューティング:
    • item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); のように、必要なフラグが正しく設定されているかコードを確認する。
    • デバッガでitem->flags()の値を確認し、期待通りのフラグが立っているか検証する。
    • シーンやビューのイベント処理が、アイテムのイベント処理を妨げていないか確認する。
  • 原因:
    • setFlags()で適切なGraphicsItemFlagが設定されていない。例えば、移動させたいのにItemIsMovableが設定されていない、など。
    • 親アイテムがこれらの機能(移動、選択など)を無効にしている場合、子アイテムも影響を受けることがある。

アイテムのイベントが発火しない

  • トラブルシューティング:
    • item->setAcceptHoverEvents(true); などのイベント受付関数が呼び出されているか確認する。
    • QGraphicsScene::installEventFilter()QGraphicsView::installEventFilter() を使ってイベントフィルターを設置していないか確認し、必要であればデバッグメッセージを出力してイベントのフローを追跡する。
    • QGraphicsItem::boundingRect()QGraphicsItem::shape() の実装が、アイテムの見た目と一致しているか、特にカスタムアイテムの場合は注意して確認する。これらの関数は、衝突検出やイベント処理の領域を決定します。
  • 原因:
    • アイテムがイベントを受け入れる設定になっていない(例: acceptHoverEvents(true)が呼び出されていない)。
    • シーンやビューにイベントフィルターが設定されており、アイテムへのイベント伝播をブロックしている。
    • boundingRect()shape()の実装が不正確で、アイテムの領域が正しく認識されていない。

QGraphicsItem::ItemSendsGeometryChangesの誤解

  • トラブルシューティング:
    • このフラグは、アイテムの変更を他のコンポーネントが監視するために使用します。実際にアイテムを移動可能にするにはItemIsMovableなどを設定する必要があります。
    • カスタムアイテムの場合、itemChange()などの仮想関数をオーバーライドして、特定のジオメトリ変更イベントを処理する必要があるか確認する。
  • 原因:
    • このフラグは、アイテムのジオメトリが変更されたときにシーンに通知を送るためのもので、変更を許可するものではありません。アイテム自体が移動やサイズ変更のロジックを持つ必要があります。
  • トラブルシューティング:
    • QGraphicsScene::setSelectionArea()QGraphicsScene::setFocusItem() などのシーンの関数が、アイテムの選択状態やフォーカス状態にどのように影響するか理解する。
    • QGraphicsItem::setSelected()QGraphicsItem::setFocus() などのアイテムの関数が、シーンの状態と同期しているか確認する。
  • 原因:
    • QGraphicsSceneがアイテムの状態(選択、フォーカスなど)を管理する役割を持っていますが、その設定が適切でない場合。
  • Qt Quick Designer:
    • QMLのUIを視覚的に確認し、状態の変化が正しく表現されているか確認する。ただし、複雑なロジックやバインディングはランタイムでしかテストできない場合があります。
  • QML Profiler:
    • QMLアプリケーションのパフォーマンスボトルネック(CPU使用率、フレームレート、バインディングの評価など)を視覚的に特定する。
  • console.log() (QML) / qDebug() (C++):
    • コードの特定のポイントで変数の値や状態を出力し、プログラムの実行フローを追跡する。
  • Qt CreatorのQML/C++デバッガ:
    • ブレークポイントを設定し、ステップ実行でコードのフローを追う。
    • 変数の値をリアルタイムで検査する。
    • QMLの場合、ライブでプロパティの値を変更してテストする。


ここでは、QMLにおけるItem.stateの例を中心に説明します。

QMLにおけるItem.stateの基本例:ボタンの状態変化

最も基本的な例として、マウスが上に乗った時(ホバー時)とクリックされた時で見た目が変化するボタンを考えてみましょう。

MyButton.qml

// MyButton.qml
import QtQuick 2.15
import QtQuick.Controls 2.15 // 必要に応じて

Rectangle {
    id: root
    width: 150
    height: 60
    radius: 8
    color: "lightgray" // 通常時の色

    // マウスインタラクションを有効にする
    MouseArea {
        anchors.fill: parent
        onEntered: root.state = "hovered" // マウスがEnterされたらhovered状態へ
        onExited: root.state = "" // マウスがExitedされたらデフォルト状態へ
        onPressed: root.state = "pressed" // マウスが押されたらpressed状態へ
        onReleased: root.state = root.containsMouse ? "hovered" : "" // マウスが離されたら、まだ要素内にいればhovered、そうでなければデフォルト
    }

    Text {
        id: buttonText
        anchors.centerIn: parent
        text: "Click Me"
        font.pixelSize: 20
        color: "black"
    }

    // 状態の定義
    states: [
        State {
            name: "hovered" // ホバー状態
            // `when` 条件は使わないが、MouseAreaのイベントで直接状態を設定
            PropertyChanges { target: root; color: "lightblue" } // ホバー時の色
        },
        State {
            name: "pressed" // 押された状態
            PropertyChanges { target: root; color: "darkblue"; scale: 0.95 } // 押された時の色と縮小
            PropertyChanges { target: buttonText; color: "white" } // テキストの色も変更
        }
    ]

    // 状態遷移のアニメーション
    transitions: Transition {
        from: "*" // どの状態からでも
        to: "*" // どの状態へでも
        // `color`と`scale`プロパティの変化を100msでアニメーション
        PropertyAnimation { properties: "color,scale"; duration: 100 }
        // `buttonText.color`の変化もアニメーション
        PropertyAnimation { target: buttonText; properties: "color"; duration: 100 }
    }
}

main.qml (アプリケーションのエントリポイント)

// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Item.state Example"

    MyButton {
        anchors.centerIn: parent
    }
}

解説

  1. id: root: アイテムのIDを定義し、他の場所から参照できるようにします。
  2. color: "lightgray": rootアイテムのデフォルト(ベース)の色です。
  3. MouseArea: マウスイベントを検出するために使用します。
    • onEntered, onExited, onPressed, onReleased シグナルハンドラ内で、root.stateプロパティに直接状態名を代入しています。これにより、アイテムの状態が切り替わります。
    • root.state = "" は、空文字列でデフォルトの状態に戻すことを意味します。
  4. states: [...]:
    • Stateオブジェクトのリストを定義します。各Stateは名前を持ち、その状態になったときに変更されるプロパティをPropertyChangesブロック内に記述します。
    • PropertyChanges { target: root; color: "lightblue" }: rootというIDを持つアイテムのcolorプロパティを"lightblue"に変更します。
    • scale: 0.95: アイテムの拡大率を0.95倍にします。
    • PropertyChanges { target: buttonText; color: "white" }: targetプロパティを使って、rootの子要素であるbuttonTextの色も変更しています。
  5. transitions: Transition { ... }:
    • 状態が切り替わる際に、プロパティの変化をアニメーションさせるための設定です。
    • from: "*"to: "*" は、どの状態からどの状態への遷移でもこのTransitionを適用するという意味です。
    • PropertyAnimation { properties: "color,scale"; duration: 100 }: rootcolorscaleプロパティが100ミリ秒かけてスムーズに変化します。
    • PropertyAnimation { target: buttonText; properties: "color"; duration: 100 }: buttonTextcolorプロパティも同様にアニメーションさせます。

このコードを実行すると、マウスをボタンの上に置くと色が変わり、クリックするとさらに色が濃くなり、少し縮むアニメーションが見られます。

QMLにおける State の応用例:複数のプロパティと条件による状態管理

今度は、特定の条件(例:プロパティの値)に基づいて状態を切り替える例を見てみましょう。例えば、アイテムが「有効 (enabled)」か「無効 (disabled)」かで見た目を変える場合です。

InteractiveItem.qml

// InteractiveItem.qml
import QtQuick 2.15

Rectangle {
    id: itemRoot
    width: 200
    height: 100
    color: "green" // デフォルト(有効時)の色
    border.color: "darkgreen"
    border.width: 2
    radius: 10

    // アイテムが有効かどうかを示すプロパティ
    property bool itemEnabled: true

    Text {
        id: statusText
        anchors.centerIn: parent
        font.pixelSize: 24
        color: "white"
        text: itemRoot.itemEnabled ? "有効" : "無効"
    }

    // クリックでitemEnabledを切り替えるMouseArea
    MouseArea {
        anchors.fill: parent
        onClicked: itemRoot.itemEnabled = !itemRoot.itemEnabled
    }

    // 状態の定義
    states: [
        State {
            name: "disabled" // 無効状態
            when: !itemRoot.itemEnabled // itemEnabledがfalseのとき
            PropertyChanges { target: itemRoot; color: "lightgray"; border.color: "darkgray" } // 色を変更
            PropertyChanges { target: statusText; color: "gray" } // テキスト色を変更
            // disabled状態ではマウスイベントを無効にする
            // MouseAreaのenabledプロパティを制御することで実現
            PropertyChanges { target: itemRoot.findChild("MouseArea"); enabled: false }
        }
    ]

    // 状態遷移のアニメーション
    transitions: Transition {
        from: "*"
        to: "*"
        // colorプロパティとborder.colorプロパティをアニメーション
        PropertyAnimation { properties: "color,border.color"; duration: 200 }
        // テキストのcolorプロパティもアニメーション
        PropertyAnimation { target: statusText; properties: "color"; duration: 200 }
    }
}

main.qml

// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: "Conditional State Example"

    InteractiveItem {
        anchors.centerIn: parent
    }
}

解説

  1. property bool itemEnabled: true: itemRootitemEnabledというカスタムプロパティを定義します。これが状態を制御するトリガーになります。
  2. onClicked: itemRoot.itemEnabled = !itemRoot.itemEnabled: MouseAreaがクリックされるたびにitemEnabledの値を反転させます。
  3. State { name: "disabled"; when: !itemRoot.itemEnabled }:
    • name: "disabled" で無効状態を定義します。
    • when: !itemRoot.itemEnabled が重要です。これは、itemRoot.itemEnabledfalseのときにこのdisabled状態が自動的に適用されることを意味します。when条件がfalseに戻ると、アイテムはデフォルトの状態に戻ります。
  4. PropertyChanges { target: itemRoot.findChild("MouseArea"); enabled: false }:
    • 無効状態ではアイテムのインタラクションを止めたいので、MouseAreaenabledプロパティをfalseに設定します。
    • findChild("MouseArea") は、QMLの実行時にIDではない子要素を名前で検索する一般的な方法です。この場合、MouseAreaにIDを付けて直接参照することもできます。

この例では、when条件を使ってプログラムの状態(itemEnabledプロパティ)に基づいてUIの状態を切り替える方法を示しています。

QGraphicsItemにおけるItem.stateは、QMLのStateとは概念が異なり、主にアイテムの挙動を制御するフラグとして使われます。

main.cpp

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QDebug> // デバッグ出力用

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

    // シーンの作成
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300); // シーンのサイズを設定

    // QGraphicsRectItemの作成
    QGraphicsRectItem *rectItem = new QGraphicsRectItem(50, 50, 100, 100);
    rectItem->setBrush(Qt::blue);
    rectItem->setPen(QPen(Qt::darkBlue, 2));

    // ここが「Item.state」に関連する部分:フラグの設定
    // ItemIsMovable: マウスでドラッグして移動可能にする
    // ItemIsSelectable: 選択可能にする(マウスでクリックして選択、Ctrl+クリックで複数選択など)
    // ItemIsFocusable: キーボードフォーカスを受け取れるようにする
    rectItem->setFlags(QGraphicsItem::ItemIsMovable |
                       QGraphicsItem::ItemIsSelectable |
                       QGraphicsItem::ItemIsFocusable);

    // デバッグ出力で現在のフラグを確認
    qDebug() << "Initial flags:" << rectItem->flags();

    // シーンにアイテムを追加
    scene.addItem(rectItem);

    // ビューの作成とシーンの設定
    QGraphicsView view(&scene);
    view.setWindowTitle("QGraphicsItem ItemState (Flags) Example");
    view.show();

    // 実行中にフラグを変更する例 (例えば、キーイベントやボタンクリックで)
    // ここでは、ボタンを押すと移動可能フラグを切り替える例を考えます
    // 実際にはUI要素と連携させることが多い
    QPushButton toggleMovableButton("Toggle Movable");
    QObject::connect(&toggleMovableButton, &QPushButton::clicked, [&]() {
        // 現在のフラグを取得し、ItemIsMovableフラグを反転させる
        QGraphicsItem::GraphicsItemFlags currentFlags = rectItem->flags();
        if (currentFlags & QGraphicsItem::ItemIsMovable) {
            rectItem->setFlags(currentFlags & ~QGraphicsItem::ItemIsMovable); // ItemIsMovableをオフにする
            qDebug() << "Item is now NOT movable.";
            toggleMovableButton.setText("Make Movable");
        } else {
            rectItem->setFlags(currentFlags | QGraphicsItem::ItemIsMovable); // ItemIsMovableをオンにする
            qDebug() << "Item is now MOVABLE.";
            toggleMovableButton.setText("Toggle Movable");
        }
    });

    // 簡単なレイアウトでボタンを配置
    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(&view);
    layout->addWidget(&toggleMovableButton);
    window.show();

    return app.exec();
}

解説

  1. rectItem->setFlags(...): QGraphicsItem::GraphicsItemFlag列挙型で定義されているフラグをsetFlags()関数に渡します。
    • QGraphicsItem::ItemIsMovable: このフラグを設定すると、マウスでアイテムをドラッグして移動できるようになります。
    • QGraphicsItem::ItemIsSelectable: このフラグを設定すると、アイテムをクリックして選択できるようになります。
    • QGraphicsItem::ItemIsFocusable: このフラグを設定すると、アイテムがキーボードフォーカスを受け取れるようになります。
  2. rectItem->flags(): 現在アイテムに設定されているフラグの集合を取得します。
  3. & ~QGraphicsItem::ItemIsMovable / | QGraphicsItem::ItemIsMovable: ビット演算子を使って、特定のフラグをオン/オフしています。これは、C++でフラグを操作する一般的な方法です。

このC++の例では、QGraphicsItemの「状態」が、オブジェクトの特性やインタラクションの挙動を定義するフラグによってどのように制御されるかを示しています。



ここでは、QMLにおけるItem.stateの代替方法と、C++ (QGraphicsItem) における代替(というよりは、そもそもの考え方)を説明します。

QMLにおける Item.state の代替方法

QMLにおけるItem.stateの主な目的は、UI要素の見た目や振る舞いを特定の「状態」に応じて一括で変更することと、その状態間の遷移をアニメーションさせることです。これに対する代替手段は以下の通りです。

Binding を直接使用する

最も直接的な代替方法です。プロパティの値が特定の条件に基づいて変化するように、直接バインディングを記述します。

  • 欠点:
    • 状態の数が増えると、Bindingの記述が冗長になる。
    • 複数のプロパティが同時に変化する場合でも、それぞれのプロパティにバインディングを書く必要がある。
    • 状態間の遷移をアニメーションさせるには、PropertyAnimationなどの要素を別途記述する必要がある。
  • 利点:
    • コードが簡潔になることが多い。
    • 明示的にプロパティと条件の関係がわかる。
  • いつ使うか:
    • 状態の数が少ない場合(2つ程度)。
    • プロパティの変化が単純で、Stateのオーバーヘッドが不要な場合。
    • 複数のプロパティが個別の条件で変化する場合。


Rectangle {
    id: myButton
    width: 100
    height: 50
    radius: 5

    // stateを使わずにhoveredプロパティに基づいて色を変化させる
    color: myButton.hovered ? "lightblue" : "lightgray"

    Text {
        anchors.centerIn: parent
        text: "Click Me"
        color: "black"
    }

    MouseArea {
        anchors.fill: parent
        // hover時にアニメーションさせる
        onEntered: {
            // PropertyAnimationを直接使用
            // myButtonのcolorプロパティを"lightblue"にアニメーション
            myButton.PropertyAnimation {
                target: myButton
                property: "color"
                to: "lightblue"
                duration: 100
            }
        }
        onExited: {
            myButton.PropertyAnimation {
                target: myButton
                property: "color"
                to: "lightgray"
                duration: 100
            }
        }
        // state="pressed"の代わりに直接変更
        onPressed: {
            myButton.color = "darkgray"
            myButton.scale = 0.95
        }
        onReleased: {
            // リリース時にホバー状態を考慮して色を戻す
            myButton.color = myButton.containsMouse ? "lightblue" : "lightgray"
            myButton.scale = 1.0
        }
    }
}

When句を持たない State と手動での状態設定

Stateオブジェクトのwhenプロパティを使わず、MouseAreaのイベントハンドラやJavaScript関数などでItem.state = "stateName"のように直接状態を設定する方法です。これは、Stateの基本的な構造を利用しつつ、状態遷移のトリガーをより細かく制御したい場合に有効です。

  • 欠点:
    • when条件による自動的な状態切り替えが失われるため、状態管理ロジックを自分で記述する必要がある。
  • 利点:
    • PropertyChangesによるプロパティの一括管理とTransitionによるアニメーションはそのまま利用できる。
    • 状態遷移のトリガーをJavaScriptで柔軟に記述できる。
  • いつ使うか:
    • 状態の数がある程度あり、PropertyChangesでプロパティを一括管理したいが、状態遷移のロジックが複雑な場合。
    • ユーザーインタラクションだけでなく、バックエンドデータや複数の条件に基づいて状態を切り替えたい場合。
    • アニメーションのTransitionメカニズムを引き続き利用したい場合。

これは、最初のMyButton.qmlの例で既に示されています。MouseAreaのイベントハンドラ内でroot.state = "hovered"のように手動で状態を設定しています。

PropertyAnimation や NumberAnimation などを直接使用する

状態変化のたびにアニメーションを直接トリガーする方法です。StateTransitionを使わずに、プロパティが変化したときにアニメーションを実行します。

  • 欠点:
    • 多くのプロパティを同時にアニメーションさせる場合に冗長になる。
    • 複雑な状態遷移のアニメーション管理が難しい。
  • 利点:
    • アニメーションの制御が非常に明示的。
    • 柔軟なアニメーションの開始・停止・一時停止などが可能。
  • いつ使うか:
    • 非常にシンプルなアニメーションで、StateTransitionの定義が過剰に感じる場合。
    • アニメーションの開始や終了のロジックが非常に特殊で、Transitionでは表現しにくい場合。
    • プロパティの変化が常にアニメーションを伴うわけではない場合。


Rectangle {
    id: animatedRect
    width: 100
    height: 100
    color: "red"

    MouseArea {
        anchors.fill: parent
        onClicked: {
            // クリックするたびに色を切り替え、アニメーションさせる
            if (animatedRect.color === "red") {
                animatedRect.color = "blue"
            } else {
                animatedRect.color = "red"
            }
        }
    }

    // colorプロパティが変化したときに自動的にアニメーションさせる
    // これはPropertyAnimationを直接記述する代替ではありませんが、
    // PropertyAnimationを特定のプロパティに関連付ける別の方法です
    Behavior on color {
        ColorAnimation { duration: 500 }
    }
}

この例では、Behavior on color を使うことで、color プロパティが変更されるたびに自動的にアニメーションが適用されます。これは、StateTransitionの組み合わせに近い効果を、より個別のプロパティに適用する方法です。

Loader や StackView を使用してUI要素を切り替える

これは「状態」というよりは「ビュー」の切り替えに近いですが、異なるUI要素(それぞれが独自の見た目と振る舞いを持つ)をロード/アンロードすることで、複雑な状態を表現する方法です。

  • 欠点:
    • 状態が単純なプロパティ変更だけで済む場合に、オーバーヘッドが大きい。
    • シームレスなアニメーション遷移を実装するには、別途アニメーションロジックが必要になる場合がある。
  • 利点:
    • 各「状態」を独立したQMLファイルとして管理でき、コードのモジュール性が高まる。
    • 複雑なUIの切り替えに対応しやすい。
  • いつ使うか:
    • 状態間でUI要素の構造が大きく異なる場合。
    • 特定の状態でのみ大量のリソースが必要な場合(必要な時だけロードすることでメモリを節約)。
    • タブ切り替えやウィザードのようなフロー。


Column {
    width: 300
    height: 200

    property string currentView: "ViewA.qml" // 表示するビューのパス

    Button {
        text: "Switch View"
        onClicked: {
            currentView = (currentView === "ViewA.qml") ? "ViewB.qml" : "ViewA.qml"
        }
    }

    Loader {
        id: viewLoader
        anchors.fill: parent
        anchors.topMargin: 50
        source: currentView // currentViewプロパティに基づいてQMLファイルをロード
    }
}

QGraphicsItemの場合、「状態」という概念はQMLのStateのように宣言的なものではなく、アイテムのプロパティ(フラグやカスタムプロパティ)を直接C++コードで操作することになります。

フラグ (GraphicsItemFlag) の直接設定/解除

これが最も一般的な「状態」の制御方法です。

  • flags(): 現在設定されているフラグの集合を取得します。
  • setFlag(QGraphicsItem::GraphicsItemFlag flag, bool enabled = true): 特定のフラグを個別に設定または解除します。
  • setFlags(QGraphicsItem::GraphicsItemFlags flags): 複数のフラグを一度に設定します。


// 例えば、マウスイベントハンドラ内で
void MyRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    // 現在の選択状態を反転
    setSelected(!isSelected());
    // もしアイテムが移動可能でなければ、移動可能にする
    if (!(flags() & QGraphicsItem::ItemIsMovable)) {
        setFlag(QGraphicsItem::ItemIsMovable, true);
        qDebug() << "Item is now movable.";
    }
    QGraphicsRectItem::mousePressEvent(event);
}

カスタムプロパティと仮想関数のオーバーライド

より複雑なアイテムの状態を管理するには、カスタムプロパティを定義し、必要に応じてitemChange()などの仮想関数をオーバーライドして、状態の変化に対応するロジックを記述します。

  • カスタムプロパティ(例: bool m_active;)を追加し、その値に基づいてpaint()関数で見た目を変更したり、他の挙動を制御したりします。
  • itemChange(GraphicsItemChange change, const QVariant &value): アイテムのプロパティや状態が変化したときに呼び出されます。ここでカスタムロジックを記述できます。


// MyCustomItem.h
class MyCustomItem : public QGraphicsRectItem
{
public:
    MyCustomItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = nullptr);

protected:
    // この関数をオーバーライドしてアイテムの状態変化を監視
    QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

private:
    bool m_isActive; // カスタムの状態プロパティ
};

// MyCustomItem.cpp
MyCustomItem::MyCustomItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent)
    : QGraphicsRectItem(x, y, width, height, parent), m_isActive(false)
{
    setFlags(QGraphicsItem::ItemIsSelectable); // 選択可能にする
}

QVariant MyCustomItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemSelectedHasChanged) {
        // 選択状態が変化した場合
        m_isActive = value.toBool(); // isSelected() の値を取得
        qDebug() << "Item selection changed. Is active:" << m_isActive;
        update(); // 再描画を要求して見た目を更新
    }
    return QGraphicsRectItem::itemChange(change, value);
}

void MyCustomItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    if (m_isActive) {
        painter->setBrush(Qt::green); // アクティブな場合は緑
    } else {
        painter->setBrush(Qt::red);   // そうでなければ赤
    }
    painter->setPen(pen());
    painter->drawRect(rect());
}

この例では、ItemSelectedHasChangedというGraphicsItemChangeを使って、アイテムが選択されたときにカスタムのm_isActiveプロパティを更新し、paint()メソッドで見た目を変更しています。