QMLプログラミング入門: Item.visibleChildrenを使った実践コード例

2025-06-06

簡単に言うと、Item.visibleChildrenは、あるItemのすべての子要素の中で、現在表示されている(visibleプロパティがtrueである)子要素のリストを提供します。

もう少し詳しく説明します。

  • 注意点 visibleChildrenは動的に変化するリストであり、子要素のvisibleプロパティが変更されると、このリストも自動的に更新されます。

  • 使用例 visibleChildrenプロパティは、例えば以下のようなシナリオで役立ちます。

    • 親要素の表示/非表示の制御
      ある親要素を、そのすべての子要素が非表示になったときに自動的に非表示にしたい場合などに使えます。例えば、子要素が一つも表示されていない場合は親も隠す、といったロジックを実装できます。

      Item {
          id: parentItem
          width: 200
          height: 200
          color: "lightgray"
          // 子要素が1つでも表示されていればparentItemも表示
          visible: parentItem.visibleChildren.length > 0
      
          Rectangle {
              id: child1
              width: 50
              height: 50
              color: "red"
              visible: true // 初期状態では表示
          }
      
          Rectangle {
              id: child2
              width: 50
              height: 50
              color: "blue"
              visible: false // 初期状態では非表示
          }
      
          MouseArea {
              anchors.fill: parent
              onClicked: {
                  // クリックするたびにchild1の表示/非表示を切り替える
                  child1.visible = !child1.visible;
              }
          }
      }
      

      上記の例では、parentItemvisibleプロパティがvisibleChildren.length > 0にバインドされています。つまり、parentItemは、child1child2などの子要素のうち、少なくとも1つがvisibleであれば表示され、すべての子要素がvisible: falseになると自身も非表示になります。

    • 表示されている子要素の数を数える
      現在表示されている子要素の数を把握したい場合に利用できます。

    • 表示されている子要素をループ処理する
      表示されている子要素に対してのみ何らかの処理を行いたい場合に便利です。

  • childrenプロパティとの違い Itemにはchildrenというプロパティもあります。これは、そのItemのすべての子要素のリスト(表示されているかどうかにかかわらず)を返します。 一方、visibleChildrenは、そのchildrenの中から現在画面に表示されているものだけをフィルタリングしてリストとして提供します。

  • 親子関係 QMLでは、要素は階層構造で配置されます。あるItemの中に別のItemを定義すると、それは親子の関係になります。親のItemが非表示になると、その子要素も自動的に非表示になります。

  • Itemとは? Qt Quick (QML) において、Itemはすべての視覚的な要素の基本となるタイプです。ボタン、テキスト、画像、矩形など、画面に表示されるものはすべてItemから派生しています。Item自体は視覚的な表現を持ちませんが、位置 (x, y)、サイズ (width, height)、アンカー (anchors)、キーイベント処理など、視覚要素に共通の属性を定義します。



親要素が非表示になると子要素のvisible設定が無視される問題

これがItem.visibleChildrenを使用する上で最も陥りやすい、そして最も重要な注意点です。

エラー/問題点
Item.visibleChildrenを使って親要素のvisibleプロパティを制御している場合(例: parentItem.visible: parentItem.visibleChildren.length > 0)、いったん親要素が非表示になると、その子要素のvisibleプロパティをtrueにしても、親要素が非表示のままであるため、子要素も表示されなくなってしまうことがあります。

なぜ起こるのか
Qt Quickのレンダリングメカニズムでは、親要素がvisible: falseである場合、その子要素はたとえ自身のvisibleプロパティがtrueであっても、画面には描画されません。Item.visibleChildrenは「現在表示されている子要素のリスト」なので、親が非表示になるとこのリストは空になり、結果として親も非表示のままになってしまう、という循環的な問題が発生します。

トラブルシューティング/解決策

  • 親のvisibleプロパティの制御を別の方法で行う
    visibleChildrenを直接使って親のvisibleを決定するのではなく、例えば「特定のボタンが押されたら親を表示する」といった明示的な制御ロジックを導入します。

  • 「表示したい」という意図を持つ別のプロパティを用意する
    これが最も推奨される解決策です。各子要素に「表示したいかどうか」を示す別のカスタムプロパティ(例: wantsToBeVisible: bool)を持たせ、実際のvisibleプロパティをこのカスタムプロパティにバインドします。そして、親要素のvisibleプロパティは、子要素のwantsToBeVisibleプロパティに基づいて決定します。

    // 子要素
    Rectangle {
        id: child1
        property bool wantsToBeVisible: true // 表示したいかどうか
        visible: wantsToBeVisible // 実際の表示/非表示はwantsToBeVisibleに依存
    
        MouseArea {
            anchors.fill: parent
            onClicked: child1.wantsToBeVisible = !child1.wantsToBeVisible; // クリックで表示/非表示の意図を切り替える
        }
    }
    
    // 親要素
    Item {
        id: parentItem
        width: 200
        height: 200
        color: "lightgray"
    
        // 親は、子要素のいずれかが表示「したい」場合に表示
        // (visibleChildrenではなく、childrenをループしてwantsToBeVisibleを確認)
        visible: {
            let anyChildWantsToBeVisible = false;
            for (let i = 0; i < children.length; i++) {
                if (children[i].wantsToBeVisible) { // カスタムプロパティを参照
                    anyChildWantsToBeVisible = true;
                    break;
                }
            }
            return anyChildWantsToBeVisible;
        }
    
        // ここに子要素を配置
        Rectangle {
            id: childA
            width: 50; height: 50; color: "red"
            property bool wantsToBeVisible: true
            visible: wantsToBeVisible
            MouseArea { anchors.fill: parent; onClicked: parent.wantsToBeVisible = !parent.wantsToBeVisible; }
        }
        Rectangle {
            id: childB
            width: 50; height: 50; color: "blue"
            property bool wantsToBeVisible: false
            visible: wantsToBeVisible
            MouseArea { anchors.fill: parent; onClicked: parent.wantsToBeVisible = !parent.wantsToBeVisible; }
        }
    }
    

    このアプローチでは、親のvisiblefalseになっても子要素のwantsToBeVisibletrueのままであり、親のvisibletrueに戻ったときに、wantsToBeVisibletrueである子要素も自動的に表示されます。

visibleChildrenが期待通りに更新されない/同期の問題

エラー/問題点
子要素のvisibleプロパティが変更されても、すぐに親のvisibleChildrenのリストに反映されないように見えることがあります。特に、QMLのバインディングの評価タイミングや、アニメーション中などに発生することがあります。

なぜ起こるのか
QMLのバインディングは非同期的に評価される場合があり、また、visibleChildrenはレンダリングの状態と密接に関連しているため、ごくわずかな遅延が生じることがあります。通常は問題になりませんが、厳密なタイミングが要求される場合に問題となることがあります。

トラブルシューティング/解決策

  • 子要素のvisibleChangedシグナルを監視する
    個々の子要素のvisibleプロパティの変更を直接監視し、それに基づいて親のロジックを更新することも可能です。ただし、子要素が多い場合はコードが複雑になる可能性があります。
  • visibleChildrenのサイズを頻繁にポーリングしない
    バインディングでparentItem.visibleChildren.length > 0のように使うのは問題ありませんが、JavaScriptのループなどで頻繁にvisibleChildrenを走査するようなコードは、パフォーマンスに影響を与える可能性があります。必要なときだけ評価するように心がけましょう。
  • onVisibleChildrenChangedシグナルを利用する
    ItemにはonVisibleChildrenChangedシグナルがあります。このシグナルは、visibleChildrenリストが変更されたときに発行されます。このシグナルハンドラ内で必要なロジックを記述することで、確実に最新の状態に対応できます。
    Item {
        id: parentItem
        onVisibleChildrenChanged: {
            console.log("Visible children count:", parentItem.visibleChildren.length);
            // ここでvisibleChildrenの変更に応じた処理を行う
        }
        // ...
    }
    

visibleChildrenが空になる場合の考慮不足

エラー/問題点
visibleChildren.lengthが0になるケースを想定せずにコードを書くと、期待しない挙動になることがあります。

なぜ起こるのか
すべての可視の子要素が非表示になった場合、visibleChildrenリストは空になります。この状態を適切にハンドリングしないと、例えば「リストが空になったらエラー」といったロジックが発動する可能性があります。

トラブルシューティング/解決策

  • nullやundefinedのチェック
    通常、visibleChildrenプロパティ自体がnullundefinedになることはありませんが、念のため存在チェックを行うことで、より堅牢なコードになります(ただし、QMLのプロパティアクセスではあまり必要ないことが多いです)。
  • lengthプロパティを安全にチェックする
    visibleChildren.lengthは、リストが空の場合に0を返します。この値を適切に利用して、リストが空である場合のロジックを実装します。
    Text {
        text: parentItem.visibleChildren.length === 0 ? "表示中の子要素はありません" : "表示中の子要素があります"
    }
    

エラー/問題点
非常に多くの要素を持つ複雑なQMLツリーでvisibleChildrenを頻繁に利用したり、そのリストをループ処理したりすると、パフォーマンスの問題を引き起こす可能性があります。

なぜ起こるのか
visibleChildrenは動的に生成されるリストであり、その維持にはある程度のオーバーヘッドがかかります。特に、リストの要素数が多い場合に、そのリストを頻繁に走査するような処理は、CPUサイクルを消費します。

トラブルシューティング/解決策

  • QML Profilerの活用
    Qt Creatorに付属のQML Profilerを使用して、アプリケーションのパフォーマンスボトルネックを特定し、visibleChildren関連の処理が原因であるかを確認します。
  • 代替手段の検討
    もし、visibleChildrenの目的が単に「すべての子要素に同じ処理を適用したい」だけであれば、Itemchildrenプロパティ(表示状態に関わらずすべての子要素を含む)をループする方が、意図が明確でパフォーマンスも予測しやすい場合があります。
  • 必要なときにだけvisibleChildrenを使用する
    常にvisibleChildrenの値を監視する必要がない場合は、onVisibleChildrenChangedシグナルや、特定のイベント発生時にのみ評価するなど、利用頻度を抑えます。


例1: 親要素の表示/非表示を、表示されている子要素の有無に連動させる

これは最も一般的なユースケースですが、前回の説明で触れた「循環参照の問題」に注意が必要です。まずはその問題が起こる例を示し、次にそれを解決する方法を示します。

問題が起こる例(非推奨)
このコードは、親要素が一度非表示になると、子要素をvisible: trueにしても表示されなくなる問題があります。

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

Window {
    width: 600
    height: 400
    visible: true
    title: "VisibleChildren Problematic Example"

    Rectangle {
        id: parentItem
        width: 300
        height: 200
        color: "lightgray"
        anchors.centerIn: parent

        // 問題点: 親のvisibleをvisibleChildrenにバインドしている
        // 親が非表示になるとvisibleChildrenは空になり、親も表示されないままになる
        visible: parentItem.visibleChildren.length > 0 ? true : false

        Text {
            id: statusText
            anchors.bottom: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Visible Children: " + parentItem.visibleChildren.length
            color: "black"
            font.pixelSize: 16
        }

        Rectangle {
            id: childA
            width: 80
            height: 80
            color: "red"
            x: 20; y: 20
            visible: true // 最初は表示

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    childA.visible = !childA.visible;
                    console.log("Child A visible:", childA.visible);
                }
            }
            Text { anchors.centerIn: parent; text: "Child A" }
        }

        Rectangle {
            id: childB
            width: 80
            height: 80
            color: "blue"
            x: 120; y: 60
            visible: true // 最初は表示

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    childB.visible = !childB.visible;
                    console.log("Child B visible:", childB.visible);
                }
            }
            Text { anchors.centerIn: parent; text: "Child B" }
        }
    }

    Button {
        text: "Toggle Parent"
        anchors.top: parentItem.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            // このボタンで親のvisibleを直接切り替えても、上記のバインディングが邪魔をする
            // parentItem.visible = !parentItem.visible; // これは意味がない
            console.log("Attempting to toggle parent's visible status...");
        }
    }
}

このコードを実行し、Child AChild Bの両方を非表示にすると、parentItemも非表示になります。その後、再度Child AChild Bをクリックしても、parentItemが非表示のままであるため、子要素も表示されません。

解決策(推奨):wantsToBeVisibleのような「意図」プロパティを使用する

親のvisibleを子要素のvisibleChildrenに直接バインドするのではなく、各子要素に「表示したい」という意図を表すカスタムプロパティを持たせ、親はその意図に基づいて自身のvisibleを決定します。

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

Window {
    width: 600
    height: 400
    visible: true
    title: "VisibleChildren Recommended Example"

    Rectangle {
        id: parentItem
        width: 300
        height: 200
        color: "lightgray"
        anchors.centerIn: parent

        // カスタムプロパティを使用して、親のvisibleを制御
        // visibleChildrenではなく、childrenをループして子要素のwantsToBeVisibleを確認
        visible: {
            let anyChildWantsToBeVisible = false;
            for (let i = 0; i < children.length; i++) {
                if (children[i].wantsToBeVisible) { // カスタムプロパティを参照
                    anyChildWantsToBeVisible = true;
                    break;
                }
            }
            return anyChildWantsToBeVisible;
        }

        Text {
            id: statusText
            anchors.bottom: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Visible Children: " + parentItem.visibleChildren.length
            color: "black"
            font.pixelSize: 16
        }

        Component {
            id: coloredRectangleComponent
            Rectangle {
                width: 80
                height: 80
                property bool wantsToBeVisible: true // 表示したいかどうかの意図
                visible: wantsToBeVisible // 実際の表示/非表示はこの意図にバインド

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        parent.wantsToBeVisible = !parent.wantsToBeVisible;
                        console.log("Child", parent.color, "wantsToBeVisible:", parent.wantsToBeVisible);
                        console.log("Child", parent.color, "visible:", parent.visible);
                    }
                }
                Text { anchors.centerIn: parent; text: parent.color; color: "white" }
            }
        }

        Loader {
            sourceComponent: coloredRectangleComponent
            x: 20; y: 20
            // 初期状態はwantsToBeVisible: true なので表示
        }

        Loader {
            sourceComponent: coloredRectangleComponent
            x: 120; y: 60
            // 初期状態はwantsToBeVisible: true なので表示
        }

        Loader {
            sourceComponent: coloredRectangleComponent
            x: 20; y: 120
            // 初期状態はwantsToBeVisible: true なので表示
            onLoaded: item.wantsToBeVisible = false // 最初に非表示にしたい場合
        }
    }
}

この例では、各子要素(coloredRectangleComponentのインスタンス)がwantsToBeVisibleというプロパティを持っています。親のvisibleは、子要素のwantsToBeVisibleプロパティをチェックすることで決定されます。これにより、親が一度非表示になっても、子要素のwantsToBeVisibletrueになれば、親も再表示され、それに伴い子要素も表示されるようになります。

例2: onVisibleChildrenChangedシグナルを利用する

visibleChildrenリストが変更されたときに特定の処理を実行したい場合に使います。

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

Window {
    width: 400
    height: 300
    visible: true
    title: "onVisibleChildrenChanged Example"

    Rectangle {
        id: container
        width: 200
        height: 200
        color: "lightblue"
        anchors.centerIn: parent

        Text {
            id: countText
            anchors.top: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
            text: "現在の表示子要素数: " + container.visibleChildren.length
            font.pixelSize: 18
            color: "black"
        }

        // visibleChildrenの変更を監視
        onVisibleChildrenChanged: {
            console.log("--- onVisibleChildrenChanged fired! ---");
            console.log("新しい表示子要素数:", container.visibleChildren.length);
            // 表示されている子要素をループして何か処理を行うことも可能
            for (let i = 0; i < container.visibleChildren.length; i++) {
                console.log("  - Item ID:", container.visibleChildren[i].id, "Color:", container.visibleChildren[i].color);
            }
        }

        // 子要素たち
        Rectangle {
            id: rect1
            width: 50; height: 50
            color: "red"
            x: 20; y: 50
            visible: true // 最初は表示
            Text { anchors.centerIn: parent; text: "1"; color: "white" }
            MouseArea { anchors.fill: parent; onClicked: parent.visible = !parent.visible; }
        }

        Rectangle {
            id: rect2
            width: 50; height: 50
            color: "green"
            x: 80; y: 80
            visible: true // 最初は表示
            Text { anchors.centerIn: parent; text: "2"; color: "white" }
            MouseArea { anchors.fill: parent; onClicked: parent.visible = !parent.visible; }
        }

        Rectangle {
            id: rect3
            width: 50; height: 50
            color: "blue"
            x: 140; y: 110
            visible: false // 最初は非表示
            Text { anchors.centerIn: parent; text: "3"; color: "white" }
            MouseArea { anchors.fill: parent; onClicked: parent.visible = !parent.visible; }
        }
    }
}

このコードを実行し、各Rectangleをクリックして表示/非表示を切り替えると、onVisibleChildrenChangedシグナルが発行され、コンソールにメッセージが表示されます。countTextも自動的に更新されます。

特定の条件を満たす、または表示されている子要素のみを対象に何か処理を行いたい場合。

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

Window {
    width: 600
    height: 400
    visible: true
    title: "Action on Visible Children Example"

    Rectangle {
        id: container
        width: 400
        height: 250
        color: "lightgreen"
        anchors.centerIn: parent

        Column {
            anchors.fill: parent
            anchors.margins: 10
            spacing: 5

            Text {
                text: "表示中の矩形を全て黄色にする"
                font.pixelSize: 18
            }

            Rectangle {
                id: rect1
                width: parent.width - 20; height: 50
                color: "red"
                visible: true
                Text { anchors.centerIn: parent; text: "Rectangle 1"; color: "white" }
                MouseArea { anchors.fill: parent; onClicked: parent.visible = !parent.visible; }
            }

            Rectangle {
                id: rect2
                width: parent.width - 20; height: 50
                color: "blue"
                visible: true
                Text { anchors.centerIn: parent; text: "Rectangle 2"; color: "white" }
                MouseArea { anchors.fill: parent; onClicked: parent.visible = !parent.visible; }
            }

            Rectangle {
                id: rect3
                width: parent.width - 20; height: 50
                color: "purple"
                visible: false
                Text { anchors.centerIn: parent; text: "Rectangle 3"; color: "white" }
                MouseArea { anchors.fill: parent; onClicked: parent.visible = !parent.visible; }
            }
        }
    }

    Button {
        text: "表示中の矩形の色を変更"
        anchors.top: container.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.topMargin: 20

        onClicked: {
            // visibleChildrenをループして色を変更
            for (let i = 0; i < container.visibleChildren.length; i++) {
                let child = container.visibleChildren[i];
                // Rectangleのみを対象とする(Textは変更しないため)
                if (child.hasOwnProperty("color")) {
                     child.color = "yellow";
                     console.log(child.id + "の色を黄色に変更しました。");
                }
            }
        }
    }
}

この例では、ボタンをクリックすると、container.visibleChildrenリストを走査し、現在表示されている矩形のみの色を黄色に変更します。Text要素はcolorプロパティを持たないため、hasOwnProperty("color")でチェックしてエラーを防いでいます。



そのため、Item.visibleChildrenに代わる、あるいは補完するプログラミング方法を検討することは重要です。以下にいくつかの代替方法と、それらをどのような状況で使うべきかを説明します。

wantsToBeVisibleのようなカスタムプロパティの使用(最も推奨)

これは、Item.visibleChildrenが抱える最も一般的な問題(親が非表示になったときに子要素を再表示できない問題)を解決するための、事実上の標準的なアプローチです。

方法

  • 親要素は、childrenプロパティを走査し、いずれかの子要素のwantsToBeVisibletrueであれば、自身のvisibletrueにします。
  • 子要素の実際のvisibleプロパティは、このwantsToBeVisibleプロパティにバインドします (visible: wantsToBeVisible)。
  • 各子要素に、その要素が「表示されることを意図しているか」を示すカスタムプロパティ(例: wantsToBeVisible: bool)を追加します。

利点

  • 柔軟な制御
    子要素の表示/非表示をより細かく、かつ直感的に制御できます。
  • 循環参照の回避
    親のvisiblefalseになっても、子要素のwantsToBeVisibleの状態は保持されるため、親が再度trueになったときに、以前表示される意図だった子要素は自動的に表示されます。
  • 明確な意図分離
    「表示したい」という意図と「実際に表示されている」という状態を明確に分離できます。

使用例

// ChildItem.qml (カスタムコンポーネントとして定義)
import QtQuick 2.0

Rectangle {
    property bool wantsToBeVisible: true // ★ 表示したいかどうかの意図
    visible: wantsToBeVisible // ★ 実際の表示は意図に依存

    // 例として、クリックで表示意図を切り替える
    MouseArea {
        anchors.fill: parent
        onClicked: parent.wantsToBeVisible = !parent.wantsToBeVisible;
    }
}

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

Window {
    width: 400
    height: 300
    visible: true
    title: "WantsToBeVisible Example"

    Rectangle {
        id: parentContainer
        width: 250
        height: 200
        color: "lightgray"
        anchors.centerIn: parent

        // 親のvisibleは、子要素のwantsToBeVisibleに基づいて決定
        visible: {
            let anyChildWantsVisible = false;
            // childrenプロパティは全ての子要素を含む
            for (let i = 0; i < children.length; i++) {
                if (children[i].hasOwnProperty("wantsToBeVisible") && children[i].wantsToBeVisible) {
                    anyChildWantsVisible = true;
                    break;
                }
            }
            return anyChildWantsVisible;
        }

        ChildItem { x: 10; y: 10; width: 60; height: 60; color: "red"; text: "A" }
        ChildItem { x: 80; y: 40; width: 60; height: 60; color: "blue"; text: "B" }
        ChildItem { x: 150; y: 70; width: 60; height: 60; color: "green"; wantsToBeVisible: false; text: "C" } // 最初から非表示の意図
    }

    Text {
        anchors.top: parentContainer.bottom
        anchors.horizontalCenter: parentContainer.horizontalCenter
        text: "表示意図の子要素数: " +
              parentContainer.children.filter(
                  child => child.hasOwnProperty("wantsToBeVisible") && child.wantsToBeVisible
              ).length
        font.pixelSize: 16
        color: "black"
    }
}

opacityプロパティの使用

Item.visibleとは異なり、opacityプロパティは親がopacity: 0になっても、子要素のvisibleプロパティの状態は影響を受けません。子要素は論理的には「表示されている」と見なされます。

方法

  • opacity: 0に設定すると、要素は見えなくなりますが、クリックイベントなどは引き続き受け取ります。完全に無効化したい場合は、enabled: falseMouseArea { visible: false }なども併用する必要があります。
  • 要素を一時的に見えなくしたいが、その下にある子要素の論理的な可視状態(visibleプロパティの値)を維持したい場合に使用します。

利点

  • スムーズなアニメーション
    opacityはアニメーションさせるのに適しており、フェードイン/アウト効果を簡単に実現できます。
  • 論理的可視状態の維持
    親が透明になっても子要素のvisibleプロパティは変更されないため、visibleChildrenの問題に悩まされずに済みます。

欠点

  • レンダリングコスト
    visible: falseと異なり、opacity: 0の要素もレンダリングパイプラインの一部に残るため、非常に多くの要素がある場合はパフォーマンスに影響を与える可能性があります。
  • イベントの受け取り
    opacity: 0でもイベントを受け取ってしまうため、明示的にイベント処理を無効にする必要がある場合があります。

使用例

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

Window {
    width: 400
    height: 300
    visible: true
    title: "Opacity Example"

    Rectangle {
        id: container
        width: 250
        height: 200
        color: "lightgray"
        anchors.centerIn: parent

        // opacityを切り替えることで表示/非表示を制御
        property bool isReallyVisible: true
        opacity: isReallyVisible ? 1 : 0

        // containerのイベントを有効/無効にする(オプション)
        // MouseArea {
        //     anchors.fill: parent
        //     enabled: container.isReallyVisible // containerのisReallyVisibleに合わせて有効/無効
        // }

        Rectangle {
            id: rect1
            width: 80; height: 80
            color: "red"
            x: 20; y: 20
            visible: true // visibleは常にtrue
            MouseArea { anchors.fill: parent; onClicked: console.log("Rect1 clicked!"); }
        }

        Rectangle {
            id: rect2
            width: 80; height: 80
            color: "blue"
            x: 100; y: 80
            visible: true // visibleは常にtrue
            MouseArea { anchors.fill: parent; onClicked: console.log("Rect2 clicked!"); }
        }
    }

    Button {
        text: "Toggle Opacity of Container"
        anchors.top: container.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.topMargin: 20
        onClicked: container.isReallyVisible = !container.isReallyVisible;
    }
}

この例では、containerisReallyVisibleプロパティを切り替えることで、opacity1(完全に表示)または0(完全に透明)になります。containerが透明な状態でも、rect1rect2visibleプロパティはtrueのままです。

C++での可視状態の管理

より複雑なロジックや、QMLだけでは難しいパフォーマンス要件がある場合、C++で子要素の可視状態を管理し、QMLにプロパティやモデルとして公開するという方法があります。

方法

  • QQuickItem::childItems()QQuickItem::children()といったメソッドを使って、子要素のリストを取得できます。
  • C++側で、子要素のQQuickItem::isVisible()をチェックしたり、QQuickItem::visibleChanged()シグナルを接続したりして、必要なロジックを実装します。
  • カスタムのQQuickItemサブクラスを作成し、Q_PROPERTYQ_INVOKABLEメソッドを通じて可視状態をQMLに公開します。

利点

  • システムリソースへのアクセス
    C++のAPIを通じて、Qtのより低レベルな機能やシステムリソースにアクセスできます。
  • 複雑なロジックの実装
    QMLでは扱いにくい、より複雑な可視状態の依存関係やアルゴリズムを実装できます。
  • 高いパフォーマンス
    C++はQMLのJavaScriptよりも高速な処理が可能です。

欠点

  • 学習コスト
    QMLだけでなくC++とQt QuickのC++ APIについての知識が必要です。
  • 開発の複雑さ
    QMLとC++間のインターフェースの設計や、コンパイル・デプロイの手順が増えます。

もし、子要素の可視状態がデータモデルによって制御される場合、RepeaterListViewのようなビュー要素で、モデルのデータをフィルタリングする方が効率的で分かりやすい場合があります。

方法

  • RepeaterListViewmodelにこのモデルを設定し、表示する要素の数を調整したり、filter機能(DelegateModelを使用するなど)で表示されるアイテムを制限します。
  • モデル(例: ListModel)に各アイテムの可視状態を示すプロパティ(例: enabled: bool)を持たせます。

利点

  • 明確な構造
    モデルのデータを変更するだけで、表示されるアイテムが自動的に更新されます。
  • 効率的なリスト表示
    特に多数のアイテムを扱う場合、ListViewなどが提供する仮想化によってパフォーマンスが向上します。
  • データ駆動
    可視状態がデータモデルによって明確に管理されます。

使用例(概念)

import QtQuick 2.15
import QtQuick.Controls 2.15

Window {
    width: 400
    height: 300
    visible: true
    title: "Model Filter Example"

    ListModel {
        id: myModel
        ListElement { name: "Item A"; isVisible: true }
        ListElement { name: "Item B"; isVisible: true }
        ListElement { name: "Item C"; isVisible: false }
        ListElement { name: "Item D"; isVisible: true }
    }

    ListView {
        width: parent.width; height: parent.height - 50
        model: myModel
        delegate: Rectangle {
            width: ListView.view.width
            height: 40
            color: isVisible ? "lightgray" : "transparent" // isVisibleがfalseなら透明
            border.color: "black"
            visible: isVisible // ここでモデルのisVisibleを直接利用
            Text {
                text: name
                anchors.centerIn: parent
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    // モデルのisVisibleを切り替える
                    myModel.set(index, { "isVisible": !isVisible });
                }
            }
        }
    }

    Button {
        text: "Add New Item"
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: myModel.append({ "name": "New Item " + (myModel.count + 1), "isVisible": true });
    }
}

この例では、ListModelisVisibleプロパティを直接デリゲートのvisibleプロパティにバインドしています。これにより、モデルのデータに基づいて表示/非表示を制御できます。Item.visibleChildrenを明示的に使う必要はありません。

Item.visibleChildrenは特定の場面では便利ですが、特に循環参照の問題を避けるためには、「表示したい」という意図を表すカスタムプロパティ(wantsToBeVisibleなど)を使用し、親はその意図に基づいて自身の表示を決定するというアプローチが最も堅牢で推奨されます。