Qt QTreeView::collapseAll()徹底解説:ツリー表示を効率的に折りたたむ方法

2025-05-26

QTreeView は、Qtのウィジェットの一つで、階層的なデータをツリー形式で表示するために使われます。例えば、ファイルエクスプローラーのように、フォルダの中にさらにフォルダやファイルがあるような構造を表示するのに最適です。

QTreeView::collapseAll() は、この QTreeView クラスのパブリックスロット (public slot) と呼ばれる関数です。この関数を呼び出すと、現在展開されている(開いている)ツリーのすべてのアイテムが閉じられ、階層が隠された状態になります。

簡単に言うと、ツリービューに表示されているすべてのフォルダを閉じて、一番上の階層だけが見えている状態にする、ということです。

詳細

  • 対応する関数
    • expandAll(): すべてのアイテムを展開(開く)します。
    • collapse(const QModelIndex &index): 特定のアイテムだけを折りたたむことができます。
    • expand(const QModelIndex &index): 特定のアイテムだけを展開することができます。
  • 使用例
    • ツリービューの初期表示として、すべてのアイテムを閉じたい場合。
    • ユーザーが「すべて閉じる」ボタンをクリックしたときに、ツリーの状態をリセットしたい場合。
    • 何らかの操作後に、ツリービューを整理して表示したい場合。
  • 効果
    ツリービューの表示が最もコンパクトな状態になります。これにより、ユーザーは全体の構造を俯瞰しやすくなります。
  • 機能
    QTreeView 内の展開されているすべてのアイテムを折りたたむ(閉じる)機能を提供します。
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QPushButton>
#include <QVBoxLayout>

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

    // QStandardItemModel を使用してツリーデータを作成
    QStandardItemModel model;

    QStandardItem *parentItem1 = new QStandardItem("親アイテム 1");
    QStandardItem *childItem1_1 = new QStandardItem("子アイテム 1.1");
    QStandardItem *childItem1_2 = new QStandardItem("子アイテム 1.2");
    parentItem1->appendRow(childItem1_1);
    parentItem1->appendRow(childItem1_2);

    QStandardItem *grandchildItem1_1_1 = new QStandardItem("孫アイテム 1.1.1");
    childItem1_1->appendRow(grandchildItem1_1_1);

    QStandardItem *parentItem2 = new QStandardItem("親アイテム 2");
    QStandardItem *childItem2_1 = new QStandardItem("子アイテム 2.1");
    parentItem2->appendRow(childItem2_1);

    model.appendRow(parentItem1);
    model.appendRow(parentItem2);

    // QTreeView を作成し、モデルを設定
    QTreeView treeView;
    treeView.setModel(&model);

    // 初期状態で一部を展開しておく
    treeView.expandAll(); // 最初はすべて展開

    // 「すべて折りたたむ」ボタンを作成
    QPushButton collapseAllButton("すべて折りたたむ");

    // ボタンがクリックされたら treeView の collapseAll() スロットを呼び出す
    QObject::connect(&collapseAllButton, &QPushButton::clicked, &treeView, &QTreeView::collapseAll);

    // レイアウトの設定
    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(&treeView);
    layout->addWidget(&collapseAllButton);

    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("QTreeView::collapseAll() の例");
    window.resize(400, 300);
    window.show();

    return app.exec();
}


QTreeView::collapseAll() は比較的単純な関数であり、直接的なエラーが発生することは稀ですが、その動作が期待通りにならない場合や、関連するコンポーネントとの相互作用によって問題が発生することがあります。

collapseAll() を呼び出してもツリーが折りたたまれない

原因

  • データ変更通知が適切に行われていない
    カスタムモデルを使用している場合、データの変更(アイテムの追加、削除、状態変更など)をQtのモデル/ビューフレームワークに適切に通知していないと、ビューがモデルの状態を正しく反映しないことがあります。collapseAll() はビューの状態を操作するものですが、モデルが不正な状態であれば期待通りに動作しないことがあります。
  • モデルが正しく設定されていない、または空である
    QTreeView にデータモデル (QAbstractItemModel の派生クラス、例: QStandardItemModel) が正しく設定されていない、またはモデルにアイテムが全く存在しない場合、折りたたむ対象がないため何も変化しません。
  • イベントループがブロックされている
    collapseAll() はスロットであり、Qtのイベントループが処理されることで実際にUIが更新されます。もし collapseAll() を呼び出した後に、UIの更新を妨げるような時間のかかる処理が同じスレッドで行われている場合、ツリービューがすぐに折りたたまれないことがあります。これは expandAll()setExpanded() でも同様に報告される一般的な問題です。

トラブルシューティング

  • カスタムモデルの場合
    beginResetModel() / endResetModel()beginInsertRows() / endInsertRows() などの適切なモデル通知関数が、モデルのデータ変更時に呼び出されていることを確認してください。
  • モデルの確認
    QTreeView::model()nullptr でないことを確認し、モデルに実際にアイテムが存在するか(例: model->rowCount() が0より大きいか)確認してください。
  • イベントループの確認
    • collapseAll() の呼び出し後に、QApplication::processEvents() を呼び出して、一時的にイベントループを処理させると、UIがすぐに更新されるか確認できます。ただし、これは一時的なデバッグ目的であり、長期的な解決策としては、時間のかかる処理を別のスレッド(QtConcurrentQThread など)に移動することを検討すべきです。
    • UIスレッドで重い処理を行う場合は、QTimer::singleShot() を使用して collapseAll() を遅延実行し、UIがフリーズしないようにすることも有効です。

collapseAll() を呼び出すと他のアイテムが予期せず展開される/閉じられる

原因

  • 他のビューとの同期
    同じモデルを共有する複数の QTreeViewQListView などがある場合、一方のビューでの操作がもう一方のビューに影響を与える可能性があります。
  • モデルのデータ構造の変更
    collapseAll() とは直接関係ありませんが、モデルのデータ構造が collapseAll() 呼び出しの前後に頻繁に、または不適切に更新される場合、ツリーの展開/折りたたみ状態が予期しない挙動を示すことがあります。特に、行の削除 (removeRows()) などを行う際に、残りのアイテムのインデックスが変わることで、既存の展開状態が保持されなくなることがあります。

トラブルシューティング

  • デバッグ
    ステップ実行やログ出力を使って、collapseAll() が呼び出される前後のモデルの状態やビューの状態を詳細に確認し、何が予期しない挙動を引き起こしているのか特定します。
  • モデルの変更とビューの更新の順序
    モデルの変更を行う際は、ビューがその変更を適切に反映できるよう、変更通知関数を正しく使用し、必要に応じてビューの更新を待つようにしてください。

スタイルシートによる表示の問題

原因

  • スタイルシートの記述ミスや競合によって、QTreeView の描画が崩れ、正しく折りたたまれていないように見えることがあります。
  • QTreeView::branch などのスタイルシートを使用して、展開/折りたたみを示す矢印(ブランチ)を非表示にしている場合、collapseAll() 自体は動作しますが、ユーザーからは視覚的な変化がわかりにくいことがあります。

トラブルシューティング

  • drawBranches() のオーバーライド
    極端なケースですが、QTreeView を継承し、drawBranches() をオーバーライドして独自の描画ロジックを実装している場合、それが原因でブランチの表示がおかしくなっている可能性もあります。
  • スタイルシートの確認
    スタイルシートを一時的に無効にするか、関連する部分をコメントアウトして、collapseAll() の視覚的な効果が正しく現れるか確認します。

パフォーマンスの問題

原因

  • 非常に多くのアイテム
    QTreeView に数万、数十万といった非常に多くのアイテムが表示されている場合、collapseAll() を呼び出すと、すべてのアイテムの状態を更新する必要があるため、一時的にUIがフリーズしたり、処理に時間がかかったりすることがあります。
  • setUpdatesEnabled(false) / setUpdatesEnabled(true)
    collapseAll() を呼び出す前後に treeView->setUpdatesEnabled(false);treeView->setUpdatesEnabled(true); を使用して、一時的にビューの更新を停止することで、パフォーマンスを改善できる場合があります。ただし、これは慎重に使用する必要があります。
  • 遅延処理/プログレス表示
    応答性の高いUIを維持するために、QTimer を使用して collapseAll() の処理を分割したり、プログレスバーを表示してユーザーに処理中であることを知らせたりすることを検討してください。
  • 仮想リストの活用
    大量のデータを扱う場合は、QTreeView がビューポートに表示されているアイテムのみをレンダリングする「仮想リスト(virtual list)」の機能を最大限に活用してください。Qtのモデル/ビューアーキテクチャはこのために設計されていますが、モデルの実装方法によっては最適化が不十分な場合があります。


QTreeView::collapseAll() は、ツリービューのすべての展開されたアイテムを折りたたむ(閉じる)ために使用される非常に便利な関数です。ここでは、いくつかの具体的な使用例とそのコードを示します。

基本的な使用法:ボタンクリックでツリーをすべて折りたたむ

最も一般的な使用例は、ユーザーがボタンをクリックしたときにツリービューをすべて折りたたむ場合です。

#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug> // デバッグ出力用

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

    // QStandardItemModel を使用してツリーデータを作成
    QStandardItemModel model;
    model.setHorizontalHeaderLabels({"名前"}); // ヘッダーを設定

    // 親アイテムの作成
    QStandardItem *parentItem1 = new QStandardItem("親アイテム A");
    QStandardItem *parentItem2 = new QStandardItem("親アイテム B");

    // 子アイテムの作成
    QStandardItem *childItem1_1 = new QStandardItem("子アイテム A-1");
    QStandardItem *childItem1_2 = new QStandardItem("子アイテム A-2");
    parentItem1->appendRow(childItem1_1);
    parentItem1->appendRow(childItem1_2);

    // 孫アイテムの作成
    QStandardItem *grandchildItem1_1_1 = new QStandardItem("孫アイテム A-1-1");
    QStandardItem *grandchildItem1_1_2 = new QStandardItem("孫アイテム A-1-2");
    childItem1_1->appendRow(grandchildItem1_1_1);
    childItem1_1->appendRow(grandchildItem1_1_2);

    QStandardItem *childItem2_1 = new QStandardItem("子アイテム B-1");
    parentItem2->appendRow(childItem2_1);

    // トップレベルのアイテムをモデルに追加
    model.appendRow(parentItem1);
    model.appendRow(parentItem2);

    // QTreeView を作成し、モデルを設定
    QTreeView treeView;
    treeView.setModel(&model);

    // 初期状態で一部を展開しておく(動作確認のため)
    treeView.expandAll(); // すべて展開

    // 「すべて折りたたむ」ボタンを作成
    QPushButton *collapseAllButton = new QPushButton("すべて折りたたむ");

    // ボタンのクリックシグナルを treeView の collapseAll() スロットに接続
    // これにより、ボタンがクリックされるとツリービューがすべて折りたたまれます。
    QObject::connect(collapseAllButton, &QPushButton::clicked, &treeView, &QTreeView::collapseAll);

    // レイアウトの設定
    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(&treeView);
    layout->addWidget(collapseAllButton);

    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("QTreeView::collapseAll() 基本例");
    window.resize(400, 300);
    window.show();

    return app.exec();
}

解説

  1. QStandardItemModel を使用して、階層的なデータを構築します。
  2. QTreeView オブジェクトを作成し、作成したモデルを setModel() で設定します。
  3. treeView.expandAll() を呼び出して、初期状態でツリーのすべてのアイテムを展開します。これにより、collapseAll() の効果が視覚的にわかりやすくなります。
  4. QPushButton を作成し、その clicked() シグナルを QTreeViewcollapseAll() スロットに接続します。これにより、ボタンが押されるたびに、ツリービューのすべてのアイテムが閉じられます。

初期表示時にツリーをすべて折りたたんだ状態にする

アプリケーション起動時や特定の画面が表示されたときに、ツリービューを初期からすべて折りたたんだ状態にしたい場合があります。

#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QVBoxLayout>

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

    QStandardItemModel model;
    model.setHorizontalHeaderLabels({"コンテンツ"});

    // サンプルデータを作成
    QStandardItem *rootItem1 = new QStandardItem("ルート 1");
    QStandardItem *childItem1_1 = new QStandardItem("子 1.1");
    QStandardItem *childItem1_2 = new QStandardItem("子 1.2");
    rootItem1->appendRow(childItem1_1);
    rootItem1->appendRow(childItem1_2);

    QStandardItem *grandchildItem1_1_1 = new QStandardItem("孫 1.1.1");
    childItem1_1->appendRow(grandchildItem1_1_1);

    QStandardItem *rootItem2 = new QStandardItem("ルート 2");
    rootItem2->appendRow(new QStandardItem("子 2.1"));

    model.appendRow(rootItem1);
    model.appendRow(rootItem2);

    QTreeView treeView;
    treeView.setModel(&model);

    // ここが重要: 初期表示時にすべて折りたたむ
    treeView.collapseAll(); 

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(&treeView);

    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("QTreeView::collapseAll() 初期表示例");
    window.resize(400, 300);
    window.show();

    return app.exec();
}

解説
QTreeView の設定とモデルのデータ投入が終わった直後に treeView.collapseAll() を呼び出すだけで、ツリーがすべて折りたたまれた状態で表示されます。

コンテキストメニューから「すべて折りたたむ」を実行する

ツリービューの右クリックメニュー(コンテキストメニュー)から「すべて折りたたむ」機能を提供することも一般的です。

#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QVBoxLayout>
#include <QMenu>
#include <QAction>
#include <QMouseEvent> // QTreeView を継承する場合に必要

// QTreeView を継承してコンテキストメニューを実装するカスタムクラス
class MyTreeView : public QTreeView {
    Q_OBJECT // シグナルとスロットを使用するために必要

public:
    MyTreeView(QWidget *parent = nullptr) : QTreeView(parent) {
        setContextMenuPolicy(Qt::CustomContextMenu); // カスタムコンテキストメニューを有効にする
        connect(this, &MyTreeView::customContextMenuRequested,
                this, &MyTreeView::onCustomContextMenu);
    }

private slots:
    void onCustomContextMenu(const QPoint &pos) {
        QMenu menu;
        QAction *collapseAllAction = menu.addAction("すべて折りたたむ");
        QAction *expandAllAction = menu.addAction("すべて展開"); // おまけ

        QAction *selectedAction = menu.exec(mapToGlobal(pos)); // グローバル座標でメニュー表示

        if (selectedAction == collapseAllAction) {
            collapseAll(); // MyTreeView::collapseAll() を呼び出す
        } else if (selectedAction == expandAllAction) {
            expandAll(); // MyTreeView::expandAll() を呼び出す
        }
    }
};

#include "main.moc" // mocファイルをインクルードすることを忘れずに(Qtのビルドシステムが生成)

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

    QStandardItemModel model;
    model.setHorizontalHeaderLabels({"ファイル/フォルダ"});

    QStandardItem *folder1 = new QStandardItem("ドキュメント");
    folder1->appendRow(new QStandardItem("レポート.docx"));
    QStandardItem *subFolder1 = new QStandardItem("写真");
    subFolder1->appendRow(new QStandardItem("夕焼け.jpg"));
    folder1->appendRow(subFolder1);

    QStandardItem *folder2 = new QStandardItem("ダウンロード");
    folder2->appendRow(new QStandardItem("installer.exe"));

    model.appendRow(folder1);
    model.appendRow(folder2);

    MyTreeView treeView; // カスタムのMyTreeViewを使用
    treeView.setModel(&model);
    treeView.expandAll(); // 初期はすべて展開

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(&treeView);

    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("QTreeView::collapseAll() コンテキストメニュー例");
    window.resize(400, 300);
    window.show();

    return app.exec();
}
  1. MyTreeView という QTreeView を継承したカスタムクラスを作成します。
  2. コンストラクタで setContextMenuPolicy(Qt::CustomContextMenu) を設定し、カスタムコンテキストメニューの表示を有効にします。
  3. customContextMenuRequested シグナルを onCustomContextMenu スロットに接続します。このシグナルは、ユーザーがビュー内で右クリックしたときに発行されます。
  4. onCustomContextMenu スロット内で QMenu を作成し、"すべて折りたたむ" アクションを追加します。
  5. menu.exec(mapToGlobal(pos)) でメニューを表示し、ユーザーが選択したアクションを selectedAction で受け取ります。
  6. 選択されたアクションが「すべて折りたたむ」であれば、collapseAll() を呼び出します。
  7. 重要
    カスタムクラスでQ_OBJECTマクロを使用している場合、moc (Meta-Object Compiler) ツールによって生成されるコードをコンパイルするために、main.cpp と同じディレクトリに main.moc または適切なmocファイル(例: my_tree_view.moc)をインクルードする必要があります。これはQtのビルドシステムが自動的に処理してくれますが、手動でMakefileなどをいじる場合は注意が必要です。


特定のアイテムを折りたたむ/展開する: collapse(const QModelIndex &index) / expand(const QModelIndex &index) / setExpanded(const QModelIndex &index, bool expanded)

collapseAll() はツリー全体に作用しますが、特定のアイテムのみを操作したい場合は、これらの関数を使用します。

  • setExpanded(const QModelIndex &index, bool expanded): expand()collapse() の両方の機能を持ちます。expandedtrue なら展開し、false なら折りたたみます。この関数が最も汎用性が高いと言えます。

  • expand(const QModelIndex &index): 指定された QModelIndex に対応するアイテムを展開します。そのアイテムの直接の子が表示されます。

  • collapse(const QModelIndex &index): 指定された QModelIndex に対応するアイテムを折りたたみます。そのアイテムの子孫は非表示になります。

使用例
特定の親アイテムのみを折りたたみたい場合

#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QVBoxLayout>
#include <QPushButton>

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

    QStandardItemModel model;
    model.setHorizontalHeaderLabels({"アイテム"});

    QStandardItem *parent1 = new QStandardItem("親 1 (折りたたむ対象)");
    QStandardItem *child1_1 = new QStandardItem("子 1-1");
    child1_1->appendRow(new QStandardItem("孫 1-1-1"));
    parent1->appendRow(child1_1);
    parent1->appendRow(new QStandardItem("子 1-2"));

    QStandardItem *parent2 = new QStandardItem("親 2 (展開されたまま)");
    parent2->appendRow(new QStandardItem("子 2-1"));

    model.appendRow(parent1);
    model.appendRow(parent2);

    QTreeView treeView;
    treeView.setModel(&model);
    treeView.expandAll(); // すべて展開しておく

    // 親 1 の QModelIndex を取得
    QModelIndex parent1Index = model.index(0, 0); // 0行目, 0列目のアイテム

    QPushButton *collapseSpecificButton = new QPushButton("親 1 を折りたたむ");
    QObject::connect(collapseSpecificButton, &QPushButton::clicked, [&]() {
        // 特定のアイテム (parent1) を折りたたむ
        treeView.collapse(parent1Index);
        // または treeView.setExpanded(parent1Index, false);
    });

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(&treeView);
    layout->addWidget(collapseSpecificButton);

    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("特定のアイテムを折りたたむ例");
    window.resize(400, 300);
    window.show();

    return app.exec();
}

特定の深さまで展開する: expandToDepth(int depth)

expandAll() はすべての深さのアイテムを展開しますが、expandToDepth(int depth) を使用すると、指定した深さまでのアイテムのみを展開し、それより深い階層は折りたたんだ状態にできます。depth が 0 の場合、トップレベルのアイテム(invisibleRootItem の直接の子)のみが表示され、それ以上は折りたたまれます。-1 を指定すると expandAll() と同じ動作になります。

使用例
トップレベルのアイテムのみを表示し、あとはすべて折りたたむ

#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QVBoxLayout>
#include <QPushButton>

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

    QStandardItemModel model;
    model.setHorizontalHeaderLabels({"ファイル/フォルダ"});

    QStandardItem *folder1 = new QStandardItem("ドキュメント");
    QStandardItem *file1_1 = new QStandardItem("レポート.docx");
    QStandardItem *subFolder1_1 = new QStandardItem("写真");
    subFolder1_1->appendRow(new QStandardItem("夕焼け.jpg"));
    subFolder1_1->appendRow(new QStandardItem("旅行.png"));
    folder1->appendRow(file1_1);
    folder1->appendRow(subFolder1_1);

    QStandardItem *folder2 = new QStandardItem("ダウンロード");
    folder2->appendRow(new QStandardItem("installer.exe"));

    model.appendRow(folder1);
    model.appendRow(folder2);

    QTreeView treeView;
    treeView.setModel(&model);

    // 初期状態で深さ0(トップレベル)のみ展開
    treeView.expandToDepth(0); // これが QTreeView::collapseAll() の直接の代替となる場合がある

    QPushButton *expandDepthButton = new QPushButton("深さ 1 まで展開");
    QObject::connect(expandDepthButton, &QPushButton::clicked, [&]() {
        treeView.expandToDepth(1); // トップレベルとその直接の子まで展開
    });

    QPushButton *collapseAllButton = new QPushButton("すべて折りたたむ");
    QObject::connect(collapseAllButton, &QPushButton::clicked, &treeView, &QTreeView::collapseAll);


    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(&treeView);
    layout->addWidget(expandDepthButton);
    layout->addWidget(collapseAllButton);


    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("expandToDepth() の例");
    window.resize(400, 300);
    window.show();

    return app.exec();
}

解説
expandToDepth(0) は、collapseAll() と同様に、ツリーのトップレベルのアイテムだけを表示し、それらの子孫はすべて折りたたまれた状態にします。これは、特にツリーの初期表示を制御したい場合に collapseAll() の実質的な代替として機能します。

モデル/ビューアーキテクチャの活用: 展開状態をモデルに保存する

より高度な代替方法として、アイテムの展開状態をビューではなく、基になるモデル(QAbstractItemModelの派生クラス)で管理する方法があります。これは、アプリケーションの状態を保存・復元する場合や、複数のビューで同じ展開状態を共有したい場合に特に有効です。

QAbstractItemModel には展開状態を直接保存するメカニズムはありませんが、モデルのアイテムにカスタムロール(Qt::UserRole以上)を使用して展開状態を保存し、ビューの setExpanded() 関数を呼び出してその状態を反映させることができます。

概念

  1. モデルに展開状態を保存
    各アイテムのデータ(例: QStandardItem::setData()) に、そのアイテムが展開されているべきかどうかを示すフラグ(bool値など)を保存します。
  2. ビューの初期化/更新
    モデルのデータを走査し、各アイテムの展開状態フラグに基づいて QTreeView::setExpanded() を呼び出します。
  3. 展開/折りたたみイベントの処理
    QTreeView::expanded() および QTreeView::collapsed() シグナルを捕捉し、そのイベントが発生したアイテムのモデル内の展開状態フラグを更新します。

これは複雑になるため、ここでは擬似コードで示します。

// 擬似コード
class MyCustomModel : public QAbstractItemModel {
    // ... モデルのデータとメソッド ...

    // カスタムロールを定義 (例: ExpansionRole = Qt::UserRole + 1)
    enum CustomRoles {
        ExpansionStateRole = Qt::UserRole + 1
    };

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
        if (role == ExpansionStateRole) {
            // このアイテムの展開状態を返す
            // 例: return itemData[index].isExpanded;
        }
        // ... その他のロール ...
    }

    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override {
        if (role == ExpansionStateRole) {
            // このアイテムの展開状態を設定し、ビューに通知
            // 例: itemData[index].isExpanded = value.toBool();
            // emit dataChanged(index, index, {ExpansionStateRole}); // ビューに通知
            return true;
        }
        // ... その他のロール ...
    }
};

// QTreeView の設定
MyTreeView::MyTreeView(QWidget *parent) : QTreeView(parent) {
    // モデルがセットされた後
    MyCustomModel *myModel = qobject_cast<MyCustomModel*>(model());
    if (myModel) {
        // モデルの全アイテムを走査して、保存された展開状態を適用
        // 再帰的なヘルパー関数で全アイテムを処理
        reapplyExpansionState(QModelIndex()); // ルートから開始
    }

    // expanded() と collapsed() シグナルを捕捉し、モデルの展開状態を更新
    connect(this, &QTreeView::expanded, this, [myModel](const QModelIndex &index) {
        if (myModel) {
            myModel->setData(index, true, MyCustomModel::ExpansionStateRole);
        }
    });
    connect(this, &QTreeView::collapsed, this, [myModel](const QModelIndex &index) {
        if (myModel) {
            myModel->setData(index, false, MyCustomModel::ExpansionStateRole);
        }
    });
}

// 全アイテムの展開状態を再適用するヘルパー関数
void MyTreeView::reapplyExpansionState(const QModelIndex &parent) {
    MyCustomModel *myModel = qobject_cast<MyCustomModel*>(model());
    if (!myModel) return;

    for (int row = 0; row < myModel->rowCount(parent); ++row) {
        QModelIndex index = myModel->index(row, 0, parent);
        if (myModel->hasChildren(index)) {
            bool isExpanded = myModel->data(index, MyCustomModel::ExpansionStateRole).toBool();
            setExpanded(index, isExpanded);
            reapplyExpansionState(index); // 再帰的に子を処理
        }
    }
}

このアプローチは collapseAll() の直接の代替というよりは、ツリーの展開状態を永続化し、より詳細に制御するためのより高度な方法です。

  • モデルによる状態管理: アプリケーションの状態保存・復元、または複数のビュー間で展開状態を共有する必要がある場合に、より柔軟で堅牢なソリューションを提供します。
  • expandToDepth(depth): ツリーの特定の深さまでのみ展開したい場合に便利です。特に expandToDepth(0)collapseAll() と同じ視覚的効果をもたらすことがあります。
  • collapse(index) / expand(index) / setExpanded(index, bool): 特定のアイテムの展開状態を個別に制御したい場合に用います。
  • collapseAll(): 最もシンプルで、ツリー全体のアイテムを閉じる場合に最適です。