QtのQTableViewで特定の行を隠す方法: hideRow()徹底解説

2025-05-27

QTableView は、表形式のデータを表示するためのウィジェットです。スプレッドシートのように行と列で構成されており、データを整理して表示するのに役立ちます。

hideRow() メソッドは、この QTableView 内の特定の行を非表示にするために使用されます。

void QTableView::hideRow(int row) の詳細

  • (int row): 引数として int 型の row を取ります。この row は、非表示にしたい行のインデックス(番号)を指定します。行のインデックスは0から始まります。例えば、0 は最初の行、1 は2番目の行を指します。
  • hideRow: メソッドの名前で、「行を隠す」という意味です。
  • QTableView
    : このメソッドが QTableView クラスのメンバー関数であることを示しています。
  • void: この関数は戻り値を持ちません。つまり、何かを計算してその結果を返すのではなく、単に内部の状態(この場合は行の表示/非表示)を変更するだけです。

動作

hideRow(int row) を呼び出すと、指定された row の行が QTableView から視覚的に非表示になります。データ自体はモデル内に存在し続け、削除されるわけではありません。単にユーザーからは見えなくなるだけです。

非表示になった行は、showRow(int row) メソッドを呼び出すことで再度表示させることができます。

#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>

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

    // QTableViewのインスタンスを作成
    QTableView *tableView = new QTableView();

    // モデルを作成(ここではQStandardItemModelを使用)
    QStandardItemModel *model = new QStandardItemModel(5, 3); // 5行3列のモデル
    for (int row = 0; row < 5; ++row) {
        for (int col = 0; col < 3; ++col) {
            model->setItem(row, col, new QStandardItem(QString("データ %0,%1").arg(row).arg(col)));
        }
    }

    // モデルをQTableViewに設定
    tableView->setModel(model);

    // 2番目の行(インデックス1)を非表示にする
    tableView->hideRow(1);

    // QTableViewを表示
    tableView->show();

    return a.exec();
}

この例では、tableView->hideRow(1); の行が実行されると、テーブルの2番目の行(「データ 1,0」「データ 1,1」「データ 1,2」を含む行)がユーザーから見えなくなります。



hideRow() を呼び出しても行が非表示にならない

原因

  • 他の設定による上書き
    スタイルシートや他のビューの設定が、行の表示/非表示に影響を与えている可能性があります。
  • ビューの更新の問題
    QTableView が何らかの理由で再描画されていない場合、非表示の変更が画面に反映されないことがあります。
  • モデルとの同期問題
    QTableView はモデルのデータを表示します。モデルが変更されたときにビューが正しく更新されていない場合、hideRow() が意図した効果を持たないことがあります。特に、カスタムモデルを使用している場合や、モデルの変更通知を適切に発行していない場合に起こり得ます。
  • 無効なインデックス
    hideRow() に渡す行のインデックスが、モデルの行数を超えているか、負の値になっている可能性があります。QTableView は、存在しない行を非表示にしようとしても何も起こしません。

トラブルシューティング

  • デバッグ出力
    qDebug() を使用して、hideRow() が呼び出される際の行インデックスや、モデルの行数などを出力し、期待通りの値になっているか確認してください。
  • setUpdatesEnabled() の確認
    描画の最適化のために tableView->setUpdatesEnabled(false) を呼び出している場合、変更が反映されないことがあります。変更後に必ず tableView->setUpdatesEnabled(true) を呼び出してください。
  • ビューの強制更新
    tableView->viewport()->update()tableView->update() を呼び出して、ビューの再描画を強制してみてください。
  • モデルの通知
    カスタムモデルを使用している場合は、データが変更された際に layoutChanged()dataChanged() などの適切なシグナルを発行していることを確認してください。
  • インデックスの確認
    model->rowCount() でモデルの総行数を取得し、hideRow() に渡すインデックスがこの範囲内(0から rowCount() - 1 まで)にあることを確認してください。

hideRow() を使って非表示にした行が、他の操作(ソートなど)で再表示される

原因

  • プロキシモデルの使用
    QSortFilterProxyModel などのプロキシモデルを使用している場合、ビューとモデル間のマッピングが変わるため、非表示の状態が失われることがあります。
  • モデルの再構築/リセット
    ソートやフィルターなどの操作によってモデルが完全に再構築されたり、リセットされたりすると、QTableView の非表示状態がリセットされることがあります。hideRow() はビューの状態を操作するものであり、モデルのデータを操作するものではありません。

トラブルシューティング

  • 永続的な非表示状態の管理
    特定の行を常に非表示にしたい場合は、その情報をモデル自体に持たせる(例:行データに「非表示フラグ」を持つ)か、あるいはビューの永続的な状態として管理する(例:非表示にしたい行のインデックスリストを保持する)方法を検討する必要があります。そして、モデルがリセットされたり、ビューが再構築されたりするたびに、その状態をビューに再適用します。
  • プロキシモデルでのフィルタリング
    行を非表示にする目的がフィルタリングである場合、hideRow() を使用するのではなく、QSortFilterProxyModelsetFilterFixedString()filterAcceptsRow() をオーバーライドして、モデルのレベルでデータをフィルタリングすることを検討してください。これにより、ソートなどの操作を行ってもフィルタリング状態が維持されます。
  • モデル変更後の再適用
    モデルが変更(特にリセット)された後、再度 hideRow() を呼び出して非表示にしたい行を適用する必要があります。

パフォーマンスの問題(多数の行を非表示にする場合)

原因

  • 頻繁なビューの更新
    多数の行をループで非表示にしようとすると、hideRow() が呼び出されるたびにビューの更新がトリガーされ、パフォーマンスが低下することがあります。

トラブルシューティング

  • プロキシモデルによるフィルタリングの検討
    多数の行を動的に非表示/表示する場合、QSortFilterProxyModel を使用してフィルタリングを行う方が、パフォーマンス面で有利な場合があります。
  • setRowHidden() の使用
    hideRow() と同じ機能ですが、Qt 5.15以降では setRowHidden(int row, bool hide) が推奨されています。この関数も同様に、複数の呼び出しの前に setUpdatesEnabled(false) を使用することでパフォーマンスを改善できます。
  • setUpdatesEnabled(false) の使用
    複数の行を非表示にする場合は、処理を開始する前に tableView->setUpdatesEnabled(false) を呼び出し、すべての行の非表示処理が完了した後に tableView->setUpdatesEnabled(true) を呼び出すことで、ビューの更新を一時的に停止させ、パフォーマンスを向上させることができます。

原因

  • QTableView のサイズポリシーやレイアウトが、行の非表示によって適切に調整されない場合があります。
  • adjustSize() または updateGeometry() の呼び出し
    レイアウトマネージャーを使用している場合でも、明示的に tableView->adjustSize()tableView->updateGeometry() を呼び出すことで、親ウィジェットやレイアウトに再計算を促すことができます。
  • 適切なレイアウトマネージャーの使用
    QTableViewQVBoxLayoutQGridLayout などの適切なレイアウトマネージャーの中に配置していることを確認してください。レイアウトマネージャーは、ウィジェットの表示/非表示に応じて自動的にサイズを調整しようとします。


例1: 基本的な行の非表示と再表示

この例では、QTableView を作成し、いくつかのデータを設定した後、特定の行を非表示にし、その後で再度表示します。

#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTableView>
#include <QStandardItemModel>
#include <QDebug>

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

    QMainWindow window;
    QWidget *centralWidget = new QWidget(&window);
    QVBoxLayout *layout = new QVBoxLayout(centralWidget);

    // QTableView の作成
    QTableView *tableView = new QTableView();

    // QStandardItemModel の作成とデータ設定
    QStandardItemModel *model = new QStandardItemModel(5, 3); // 5行3列
    model->setHorizontalHeaderLabels({"列A", "列B", "列C"});
    for (int row = 0; row < 5; ++row) {
        for (int col = 0; col < 3; ++col) {
            model->setItem(row, col, new QStandardItem(QString("データ %0,%1").arg(row).arg(col)));
        }
    }
    tableView->setModel(model);

    layout->addWidget(tableView);

    // 2番目の行(インデックス1)を非表示にするボタン
    QPushButton *hideRowButton = new QPushButton("2番目の行を非表示 (hideRow(1))");
    QObject::connect(hideRowButton, &QPushButton::clicked, [tableView]() {
        tableView->hideRow(1);
        qDebug() << "2番目の行を非表示にしました。";
    });
    layout->addWidget(hideRowButton);

    // 2番目の行(インデックス1)を再表示するボタン
    QPushButton *showRowButton = new QPushButton("2番目の行を再表示 (showRow(1))");
    QObject::connect(showRowButton, &QPushButton::clicked, [tableView]() {
        tableView->showRow(1);
        qDebug() << "2番目の行を再表示しました。";
    });
    layout->addWidget(showRowButton);

    window.setCentralWidget(centralWidget);
    window.setWindowTitle("QTableView::hideRow() の基本");
    window.show();

    return a.exec();
}

解説

  • qDebug() を使用して、ボタンがクリックされたときにコンソールにメッセージを出力し、動作を確認できるようにしています。
  • 「2番目の行を再表示」ボタンをクリックすると、tableView->showRow(1); が呼び出され、非表示になっていた行が再び表示されます。
  • 「2番目の行を非表示」ボタンをクリックすると、tableView->hideRow(1); が呼び出され、インデックス1の行(上から2番目の行)が非表示になります。
  • QStandardItemModel を使用して簡単なデータモデルを作成し、QTableView に設定しています。

例2: 複数の行を効率的に非表示にする (setUpdatesEnabled の活用)

多数の行をループで非表示にする場合、個々の hideRow() 呼び出しごとにビューの更新が行われるとパフォーマンスが低下する可能性があります。setUpdatesEnabled(false) を使用して更新を一時停止することで、この問題を回避できます。

#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTableView>
#include <QStandardItemModel>
#include <QDebug>

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

    QMainWindow window;
    QWidget *centralWidget = new QWidget(&window);
    QVBoxLayout *layout = new QVBoxLayout(centralWidget);

    QTableView *tableView = new QTableView();
    QStandardItemModel *model = new QStandardItemModel(20, 3); // 20行3列
    model->setHorizontalHeaderLabels({"列A", "列B", "列C"});
    for (int row = 0; row < 20; ++row) {
        for (int col = 0; col < 3; ++col) {
            model->setItem(row, col, new QStandardItem(QString("データ %0,%1").arg(row).arg(col)));
        }
    }
    tableView->setModel(model);

    layout->addWidget(tableView);

    // 複数の行を非表示にするボタン (setUpdatesEnabled を使用)
    QPushButton *hideMultipleRowsButton = new QPushButton("奇数行を非表示 (setUpdatesEnabled を使用)");
    QObject::connect(hideMultipleRowsButton, &QPushButton::clicked, [tableView]() {
        qDebug() << "奇数行を非表示にしています...";
        tableView->setUpdatesEnabled(false); // 更新を一時停止

        // 奇数行を非表示にする
        for (int row = 0; row < tableView->model()->rowCount(); ++row) {
            if (row % 2 != 0) { // 奇数行のインデックス
                tableView->hideRow(row);
            }
        }
        tableView->setUpdatesEnabled(true); // 更新を再開
        tableView->viewport()->update();    // ビューポートを強制的に更新
        qDebug() << "奇数行の非表示が完了しました。";
    });
    layout->addWidget(hideMultipleRowsButton);

    // 全ての行を再表示するボタン
    QPushButton *showAllRowsButton = new QPushButton("全ての行を再表示");
    QObject::connect(showAllRowsButton, &QPushButton::clicked, [tableView]() {
        qDebug() << "全ての行を再表示しています...";
        tableView->setUpdatesEnabled(false); // 更新を一時停止
        for (int row = 0; row < tableView->model()->rowCount(); ++row) {
            tableView->showRow(row);
        }
        tableView->setUpdatesEnabled(true); // 更新を再開
        tableView->viewport()->update();    // ビューポートを強制的に更新
        qDebug() << "全ての行の再表示が完了しました。";
    });
    layout->addWidget(showAllRowsButton);

    window.setCentralWidget(centralWidget);
    window.setWindowTitle("QTableView::hideRow() の効率的な使用");
    window.show();

    return a.exec();
}

解説

  • showAllRowsButton も同様のテクニックを使用しています。
  • すべての行を非表示にした後、tableView->setUpdatesEnabled(true); で更新を有効に戻し、tableView->viewport()->update(); でビューポートの再描画を強制しています。これにより、最終的な状態が一度に描画され、パフォーマンスが向上します。
  • tableView->setUpdatesEnabled(false); を呼び出すことで、続く hideRow() の呼び出しによる即時的なビューの再描画を防ぎます。

例3: QSortFilterProxyModel を使用したフィルタリング(hideRow() の代替案)

hideRow() はビューの表示を制御しますが、データのフィルタリングを行いたい場合は QSortFilterProxyModel を使用する方が一般的で、より堅牢な方法です。ソートなどを行ってもフィルタリング状態が維持されます。

#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QTableView>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QDebug>

// カスタムのプロキシモデル
class MyFilterProxyModel : public QSortFilterProxyModel {
public:
    MyFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}

    // このメソッドをオーバーライドして、行を表示するかどうかを決定
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override {
        QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); // 最初の列のデータを基準にフィルタリング
        QString data = sourceModel()->data(index).toString();

        // 例えば、「データ 2」を含む行を非表示にする(フィルタリングする)
        if (data.contains("データ 2")) {
            return false; // 表示しない
        }
        return true; // 表示する
    }
};

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

    QMainWindow window;
    QWidget *centralWidget = new QWidget(&window);
    QVBoxLayout *layout = new QVBoxLayout(centralWidget);

    QTableView *tableView = new QTableView();
    QStandardItemModel *sourceModel = new QStandardItemModel(5, 3); // 元のモデル
    sourceModel->setHorizontalHeaderLabels({"列A", "列B", "列C"});
    for (int row = 0; row < 5; ++row) {
        for (int col = 0; col < 3; ++col) {
            sourceModel->setItem(row, col, new QStandardItem(QString("データ %0,%1").arg(row).arg(col)));
        }
    }

    // プロキシモデルの作成と設定
    MyFilterProxyModel *proxyModel = new MyFilterProxyModel();
    proxyModel->setSourceModel(sourceModel);
    tableView->setModel(proxyModel); // QTableView にはプロキシモデルを設定

    layout->addWidget(tableView);

    // プロキシモデルでのフィルタリングをトリガーするボタン (例: 動的なフィルタリング)
    QPushButton *toggleFilterButton = new QPushButton("「データ 2」を含む行をフィルタリング/表示");
    QObject::connect(toggleFilterButton, &QPushButton::clicked, [proxyModel]() {
        // ここでは簡単に、filterAcceptsRow のロジックを切り替えるようなことはできませんが、
        // 実際にはQLineEditなどからフィルタ文字列を取得して proxyModel->setFilterRegExp() を使う
        // 例えば、ここではfilterAcceptsRowの条件を一時的に変更するようなことは困難なので、
        // proxyModel->invalidateFilter() を呼び出してフィルタを再適用するイメージ
        qDebug() << "フィルタを再適用します(「データ 2」を含む行がフィルタリングされます)";
        proxyModel->invalidateFilter(); // filterAcceptsRow() が再評価される
    });
    layout->addWidget(toggleFilterButton);

    window.setCentralWidget(centralWidget);
    window.setWindowTitle("QSortFilterProxyModel を使用したフィルタリング");
    window.show();

    return a.exec();
}

解説

  • この方法は、hideRow() よりも複雑ですが、ソート、フィルタリング、およびモデルの変更に対してよりロバストなソリューションを提供します。
  • 「フィルタを再適用」ボタンをクリックすると、proxyModel->invalidateFilter() が呼び出されます。これにより、プロキシモデルはすべての行に対して filterAcceptsRow() を再評価し、ビューが更新されます。
  • QTableView には proxyModel を設定します。元の sourceModel ではありません。
  • filterAcceptsRow() の中で、特定の条件(この例では「データ 2」という文字列を含むかどうか)に基づいて、その行を表示するか (return true;)、非表示にするか (return false;) を決定します。
  • MyFilterProxyModel クラスは QSortFilterProxyModel を継承し、filterAcceptsRow() メソッドをオーバーライドしています。

これらの例が、QTableView::hideRow() の使い方と、関連する考慮事項を理解するのに役立つことを願っています。 Qt の QTableView::hideRow() メソッドに関連するプログラミング例をいくつかご紹介します。これらの例は、基本的な使い方から、少し応用的な使い方までをカバーしています。

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

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

    // メインウィンドウとレイアウト
    QWidget *window = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout(window);

    // QTableViewのインスタンスを作成
    QTableView *tableView = new QTableView();

    // モデルを作成 (QStandardItemModelを使用)
    QStandardItemModel *model = new QStandardItemModel(5, 3); // 5行3列
    for (int row = 0; row < 5; ++row) {
        for (int col = 0; col < 3; ++col) {
            model->setItem(row, col, new QStandardItem(QString("データ %0,%1").arg(row).arg(col)));
        }
    }
    tableView->setModel(model);

    // テーブルビューをレイアウトに追加
    layout->addWidget(tableView);

    // 行を非表示にするボタン
    QPushButton *hideButton = new QPushButton("2行目を非表示 (インデックス 1)");
    QObject::connect(hideButton, &QPushButton::clicked, [tableView]() {
        tableView->hideRow(1); // 2番目の行(インデックス1)を非表示
        qDebug() << "2行目を非表示にしました。";
    });
    layout->addWidget(hideButton);

    // 行を再表示するボタン
    QPushButton *showButton = new QPushButton("2行目を再表示 (インデックス 1)");
    QObject::connect(showButton, &QPushButton::clicked, [tableView]() {
        tableView->showRow(1); // 2番目の行(インデックス1)を再表示
        qDebug() << "2行目を再表示しました。";
    });
    layout->addWidget(showButton);

    window->setWindowTitle("QTableView::hideRow() 例");
    window->resize(400, 300);
    window->show();

    return a.exec();
}

解説

  1. QStandardItemModel を作成し、5行3列のサンプルデータを設定します。
  2. QTableView にこのモデルを設定します。
  3. 「2行目を非表示」ボタンをクリックすると、tableView->hideRow(1); が呼び出され、インデックス 1 の行(2番目の行)が非表示になります。
  4. 「2行目を再表示」ボタンをクリックすると、tableView->showRow(1); が呼び出され、非表示になっていた行が再び表示されます。

多数の行を一度に非表示にする場合、個別に hideRow() を呼び出すとパフォーマンスが低下する可能性があります。この問題は、setUpdatesEnabled(false) を使用してビューの更新を一時的に停止することで解決できます。

#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug>

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

    QWidget *window = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout(window);

    QTableView *tableView = new QTableView();
    QStandardItemModel *model = new QStandardItemModel(100, 3); // 100行3列のモデル
    for (int row = 0; row < 100; ++row) {
        for (int col = 0; col < 3; ++col) {
            model->setItem(row, col, new QStandardItem(QString("データ %0,%1").arg(row).arg(col)));
        }
    }
    tableView->setModel(model);
    layout->addWidget(tableView);

    QPushButton *hideMultipleButton = new QPushButton("偶数行をすべて非表示");
    QObject::connect(hideMultipleButton, &QPushButton::clicked, [tableView, model]() {
        // 更新を一時停止
        tableView->setUpdatesEnabled(false);
        qDebug() << "更新を一時停止しました。";

        for (int i = 0; i < model->rowCount(); ++i) {
            if (i % 2 == 0) { // 偶数行を非表示
                tableView->hideRow(i);
            }
        }

        // 更新を再開
        tableView->setUpdatesEnabled(true);
        qDebug() << "更新を再開しました。";
        // 必要に応じてビューの再描画を強制
        tableView->viewport()->update();
    });
    layout->addWidget(hideMultipleButton);

    QPushButton *showAllButton = new QPushButton("すべての行を再表示");
    QObject::connect(showAllButton, &QPushButton::clicked, [tableView, model]() {
        // 更新を一時停止
        tableView->setUpdatesEnabled(false);
        qDebug() << "更新を一時停止しました。";

        for (int i = 0; i < model->rowCount(); ++i) {
            tableView->showRow(i); // すべての行を再表示
        }

        // 更新を再開
        tableView->setUpdatesEnabled(true);
        qDebug() << "更新を再開しました。";
        // 必要に応じてビューの再描画を強制
        tableView->viewport()->update();
    });
    layout->addWidget(showAllButton);

    window->setWindowTitle("QTableView::hideRow() パフォーマンス例");
    window->resize(600, 400);
    window->show();

    return a.exec();
}

解説

  1. QTableView のインスタンスを取得します。
  2. 「偶数行をすべて非表示」ボタンのクリック時に、まず tableView->setUpdatesEnabled(false); を呼び出し、ビューの描画更新を無効にします。
  3. ループ内で偶数行を hideRow() で非表示にしていきます。この間、画面は更新されません。
  4. ループが完了した後、tableView->setUpdatesEnabled(true); を呼び出し、ビューの描画更新を再度有効にします。
  5. tableView->viewport()->update(); を呼び出すことで、非表示の変更が画面に即座に反映されるように強制します。

例3: モデルのデータに基づいて行を非表示にする

この例では、モデル内の特定の条件(例:特定の列の値が「非表示」の場合)に基づいて行を非表示にします。

#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug>

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

    QWidget *window = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout(window);

    QTableView *tableView = new QTableView();
    QStandardItemModel *model = new QStandardItemModel(5, 2); // 5行2列

    // データ設定
    model->setItem(0, 0, new QStandardItem("りんご")); model->setItem(0, 1, new QStandardItem("表示"));
    model->setItem(1, 0, new QStandardItem("バナナ")); model->setItem(1, 1, new QStandardItem("非表示")); // この行を非表示にする
    model->setItem(2, 0, new QStandardItem("オレンジ")); model->setItem(2, 1, new QStandardItem("表示"));
    model->setItem(3, 0, new QStandardItem("ブドウ")); model->setItem(3, 1, new QStandardItem("非表示")); // この行を非表示にする
    model->setItem(4, 0, new QStandardItem("いちご")); model->setItem(4, 1, new QStandardItem("表示"));

    // ヘッダ設定
    model->setHeaderData(0, Qt::Horizontal, "アイテム");
    model->setHeaderData(1, Qt::Horizontal, "状態");

    tableView->setModel(model);
    layout->addWidget(tableView);

    QPushButton *filterButton = new QPushButton("「非表示」の行を隠す");
    QObject::connect(filterButton, &QPushButton::clicked, [tableView, model]() {
        tableView->setUpdatesEnabled(false); // 更新を一時停止

        for (int row = 0; row < model->rowCount(); ++row) {
            QModelIndex statusIndex = model->index(row, 1); // 2列目(状態列)のインデックス
            QString status = model->data(statusIndex).toString();

            if (status == "非表示") {
                tableView->hideRow(row);
                qDebug() << "行" << row << "を非表示にしました。";
            } else {
                tableView->showRow(row); // 念のため表示に戻す
            }
        }
        tableView->setUpdatesEnabled(true); // 更新を再開
        tableView->viewport()->update();
    });
    layout->addWidget(filterButton);

    QPushButton *showAllButton = new QPushButton("すべての行を表示");
    QObject::connect(showAllButton, &QPushButton::clicked, [tableView, model]() {
        tableView->setUpdatesEnabled(false);
        for (int row = 0; row < model->rowCount(); ++row) {
            tableView->showRow(row);
        }
        tableView->setUpdatesEnabled(true);
        tableView->viewport()->update();
    });
    layout->addWidget(showAllButton);

    window->setWindowTitle("データに基づく非表示例");
    window->resize(450, 350);
    window->show();

    return a.exec();
}

解説

  1. 2列目(インデックス1)に「状態」列を作成し、「表示」または「非表示」のテキストを設定します。
  2. 「「非表示」の行を隠す」ボタンをクリックすると、モデルの各行をループし、2列目の値が「非表示」である場合に hideRow() を呼び出してその行を隠します。
  3. この例でも、複数の行を操作するため setUpdatesEnabled() を使用してパフォーマンスを最適化しています。

hideRow() はビューの直接的な操作ですが、より複雑なフィルタリングやソートを伴う非表示の場合、QSortFilterProxyModel を使用する方が強力で柔軟な解決策となります。この例では、特定の行をフィルタリングして非表示にします。

#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QDebug>

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

    QWidget *window = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout(window);

    // 元のモデル(ソースモデル)
    QStandardItemModel *sourceModel = new QStandardItemModel(5, 2);
    sourceModel->setItem(0, 0, new QStandardItem("りんご")); sourceModel->setItem(0, 1, new QStandardItem("フルーツ"));
    sourceModel->setItem(1, 0, new QStandardItem("トマト")); sourceModel->setItem(1, 1, new QStandardItem("野菜"));
    sourceModel->setItem(2, 0, new QStandardItem("バナナ")); sourceModel->setItem(2, 1, new QStandardItem("フルーツ"));
    sourceModel->setItem(3, 0, new QStandardItem("にんじん")); sourceModel->setItem(3, 1, new QStandardItem("野菜"));
    sourceModel->setItem(4, 0, new QStandardItem("オレンジ")); sourceModel->setItem(4, 1, new QStandardItem("フルーツ"));

    sourceModel->setHeaderData(0, Qt::Horizontal, "名前");
    sourceModel->setHeaderData(1, Qt::Horizontal, "カテゴリ");

    // プロキシモデルの作成
    QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel();
    proxyModel->setSourceModel(sourceModel);
    proxyModel->setFilterKeyColumn(1); // 2列目(カテゴリ列)でフィルタリング

    // QTableViewにプロキシモデルを設定
    QTableView *tableView = new QTableView();
    tableView->setModel(proxyModel);
    layout->addWidget(tableView);

    // フィルタリング用の入力フィールド
    QLabel *filterLabel = new QLabel("カテゴリでフィルタリング:");
    QLineEdit *filterLineEdit = new QLineEdit();
    layout->addWidget(filterLabel);
    layout->addWidget(filterLineEdit);

    // フィルタリング処理の接続
    QObject::connect(filterLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterRegExp);
    // フィルタリングの正規表現を大文字小文字を区別しないように設定
    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);

    window->setWindowTitle("QSortFilterProxyModelによるフィルタリング例");
    window->resize(500, 400);
    window->show();

    return a.exec();
}

解説

  1. QStandardItemModelsourceModel として作成します。これが実際のデータを持つモデルです。
  2. QSortFilterProxyModel のインスタンス proxyModel を作成し、setSourceModel()sourceModel を設定します。
  3. proxyModel->setFilterKeyColumn(1); で、2列目(カテゴリ列)をフィルタリングの対象とします。
  4. QTableView には proxyModel を設定します。ユーザーが入力フィールドにテキストを入力すると、setFilterRegExp() が呼び出され、正規表現に一致しない行が自動的に非表示になります。
  5. setFilterCaseSensitivity(Qt::CaseInsensitive); で、大文字小文字を区別しないフィルタリングを設定しています。

この例では hideRow() を直接呼び出していませんが、QSortFilterProxyModel が内部的にフィルタリングを行い、結果としてビュー上の行を非表示にする(または表示する)動作を実現しています。これは、hideRow() のようなビューの直接操作よりも、大規模なデータセットや動的なフィルタリングが必要な場合に推奨されるアプローチです。



QSortFilterProxyModel を使用したフィルタリング

これが hideRow() の最も強力で柔軟な代替手段であり、特にデータ量が多い場合や、複雑な条件で動的に行の表示/非表示を制御したい場合に推奨されます。

QSortFilterProxyModel は、ソースモデル(元のデータモデル)とビューの間に位置するプロキシモデルです。ビューはプロキシモデルからデータを取得し、プロキシモデルはソースモデルのデータをフィルタリングしたりソートしたりしてビューに提供します。

メリット

  • パフォーマンス
    大量のデータに対するフィルタリングに最適化されています。
  • ソート機能との連携
    フィルタリングされたデータに対してソートを適用できます。
  • 柔軟なフィルタリング
    正規表現、カスタムロジック(filterAcceptsRow() をオーバーライド)、複数の条件に基づいたフィルタリングが可能です。
  • データの変更と独立
    hideRow() はビューの状態を操作するのに対し、QSortFilterProxyModel はモデルのレベルでデータを見せるかどうかを制御します。これにより、ソースモデルが変更されたり、ソートされたりしても、フィルタリングのロジックが維持されます。

特定のテキストを含む行のみを表示するフィルタリングの例です。

#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QLineEdit>
#include <QVBoxLayout>

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

    QWidget *window = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout(window);

    // 元のデータモデル (ソースモデル)
    QStandardItemModel *sourceModel = new QStandardItemModel(5, 2);
    sourceModel->setItem(0, 0, new QStandardItem("Apple"));  sourceModel->setItem(0, 1, new QStandardItem("Fruit"));
    sourceModel->setItem(1, 0, new QStandardItem("Tomato")); sourceModel->setItem(1, 1, new QStandardItem("Vegetable"));
    sourceModel->setItem(2, 0, new QStandardItem("Banana")); sourceModel->setItem(2, 1, new QStandardItem("Fruit"));
    sourceModel->setItem(3, 0, new QStandardItem("Carrot")); sourceModel->setItem(3, 1, new QStandardItem("Vegetable"));
    sourceModel->setItem(4, 0, new QStandardItem("Orange")); sourceModel->setItem(4, 1, new QStandardItem("Fruit"));

    sourceModel->setHeaderData(0, Qt::Horizontal, "Name");
    sourceModel->setHeaderData(1, Qt::Horizontal, "Category");

    // プロキシモデルの作成
    QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel();
    proxyModel->setSourceModel(sourceModel);
    // 1列目 (Category) でフィルタリングする設定
    proxyModel->setFilterKeyColumn(1); 

    // QTableView にプロキシモデルを設定
    QTableView *tableView = new QTableView();
    tableView->setModel(proxyModel);
    layout->addWidget(tableView);

    // フィルタリング用の入力フィールド
    QLineEdit *filterLineEdit = new QLineEdit();
    filterLineEdit->setPlaceholderText("カテゴリをフィルタリング...");
    layout->addWidget(filterLineEdit);

    // 入力テキストが変更されたらプロキシモデルのフィルタリングを更新
    QObject::connect(filterLineEdit, &QLineEdit::textChanged,
                     proxyModel, &QSortFilterProxyModel::setFilterFixedString);
    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); // 大文字小文字を区別しない

    window->setWindowTitle("QSortFilterProxyModel Example");
    window->resize(500, 350);
    window->show();

    return a.exec();
}

このコードでは、QLineEdit に入力されたテキストに基づいて「カテゴリ」列をフィルタリングし、一致しない行は自動的に非表示になります。

カスタムモデルでの rowCount() のオーバーライド

非常に特殊なケースで、特定の行を永続的にビューから隠したい場合や、ビューに「見せるべき行」をモデル側で厳密に制御したい場合に有効です。

QAbstractTableModelQAbstractListModel などのカスタムモデルを作成し、rowCount() メソッドをオーバーライドして、実際に表示したい行の数だけを返すようにします。また、data()index() などの他のメソッドも、この新しい行数に合わせて調整する必要があります。

  • ビューの状態に依存しない。
  • モデルレベルでの厳密な制御。

デメリット

  • ソート機能との競合
    カスタムモデルでフィルタリングとソートの両方を実装するのは非常に困難です。
  • 柔軟性の欠如
    後から表示/非表示を切り替えるのが難しくなります。非表示にした行を再度表示するには、モデルの状態を再構築する必要があります。
  • 複雑性
    モデルの他の多くのメソッドも変更する必要があり、実装が複雑になります。

使用例(概念的)

これは概念的な例であり、完全な動作コードではありませんが、考え方を示します。

// MyCustomTableModel.h
#include <QAbstractTableModel>
#include <QVector>

class MyCustomTableModel : public QAbstractTableModel {
    Q_OBJECT
public:
    explicit MyCustomTableModel(QObject *parent = nullptr);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

    // 非表示にする行のインデックスを管理する関数 (例)
    void setHiddenRows(const QList<int>& hiddenRows);

private:
    QVector<QVector<QString>> m_data; // 実際のデータ
    QList<int> m_hiddenRows; // 非表示にしたい元の行インデックス
};

// MyCustomTableModel.cpp
#include "MyCustomTableModel.h"
#include <algorithm> // for std::find

MyCustomTableModel::MyCustomTableModel(QObject *parent)
    : QAbstractTableModel(parent) {
    // サンプルデータ
    m_data = {
        {"Row 0, Col 0", "Row 0, Col 1"},
        {"Row 1, Col 0", "Row 1, Col 1"}, // これを隠したい場合
        {"Row 2, Col 0", "Row 2, Col 1"}
    };
}

int MyCustomTableModel::rowCount(const QModelIndex &parent) const {
    if (parent.isValid())
        return 0;
    // 実際に表示したい行数だけを返す
    return m_data.size() - m_hiddenRows.size();
}

// ... columnCount(), data(), headerData() も、
// m_hiddenRows を考慮して、適切なインデックスマッピングを行う必要がある
QVariant MyCustomTableModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid() || role != Qt::DisplayRole)
        return QVariant();

    // 例: 非表示の行をスキップして、表示される行のインデックスに変換
    int visibleRow = index.row();
    int sourceRow = -1;
    int currentVisibleCount = 0;
    for (int i = 0; i < m_data.size(); ++i) {
        if (std::find(m_hiddenRows.begin(), m_hiddenRows.end(), i) == m_hiddenRows.end()) {
            // この行は非表示ではない
            if (currentVisibleCount == visibleRow) {
                sourceRow = i;
                break;
            }
            currentVisibleCount++;
        }
    }

    if (sourceRow != -1 && sourceRow < m_data.size() && index.column() < m_data[sourceRow].size()) {
        return m_data[sourceRow][index.column()];
    }
    return QVariant();
}

void MyCustomTableModel::setHiddenRows(const QList<int>& hiddenRows) {
    beginResetModel(); // モデルのリセットを通知
    m_hiddenRows = hiddenRows;
    endResetModel();   // モデルのリセット完了を通知 (ビューが更新される)
}

// ... その他のメソッドも同様に調整が必要です

これは非常に複雑になるため、通常は QSortFilterProxyModel を使用することを強くお勧めします。

hideRow() の代替というよりは、新しい名称とシグネチャを持つ同等のメソッドです。機能的には hideRow() と同じです。

void QTableView::setRowHidden(int row, bool hide)

hidetrue の場合、指定された行は非表示になります。false の場合、表示されます。hideRow(row)setRowHidden(row, true) と等価です。

  • hideRow()showRow() の機能を一つのメソッドでカバーできるため、コードがより明確になります。
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QPushButton>
#include <QVBoxLayout>

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

    QWidget *window = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout(window);

    QTableView *tableView = new QTableView();
    QStandardItemModel *model = new QStandardItemModel(5, 2);
    for (int r = 0; r < 5; ++r) {
        model->setItem(r, 0, new QStandardItem(QString("Item %1").arg(r + 1)));
        model->setItem(r, 1, new QStandardItem(QString("Value %1").arg(r + 1)));
    }
    tableView->setModel(model);
    layout->addWidget(tableView);

    // 2行目を非表示にする
    QPushButton *hideButton = new QPushButton("Hide Row 1 (using setRowHidden)");
    QObject::connect(hideButton, &QPushButton::clicked, [tableView]() {
        tableView->setRowHidden(1, true); // 行インデックス 1 を非表示
    });
    layout->addWidget(hideButton);

    // 2行目を表示に戻す
    QPushButton *showButton = new QPushButton("Show Row 1 (using setRowHidden)");
    QObject::connect(showButton, &QPushButton::clicked, [tableView]() {
        tableView->setRowHidden(1, false); // 行インデックス 1 を表示
    });
    layout->addWidget(showButton);

    window->setWindowTitle("QTableView::setRowHidden() Example");
    window->resize(400, 300);
    window->show();

    return a.exec();
}

  • モデルレベルでの厳密な制御(稀)
    非常に特殊な要件で、ビューではなくモデル自体が行の存在を制御する必要がある場合は、カスタムモデルで rowCount() などをオーバーライドすることも可能ですが、実装が複雑になるため注意が必要です。
  • 動的なフィルタリング、ソート、大量データ
    データの種類や条件に基づいて行をフィルタリングする必要がある場合、特にソート機能も必要な場合は、QSortFilterProxyModel が断然推奨される解決策です。これは、モデル/ビューアーキテクチャの強力な側面を利用しています。
  • 簡単な一時的な非表示
    単純に数行を非表示にしたり表示したりするだけであれば、QTableView::hideRow() または QTableView::setRowHidden() が最も直接的で簡単です。