QTableWidget::takeItem()の代替手段:Qtテーブル操作の多様な選択肢

2025-05-16

QTableWidget::takeItem(int row, int column) は、QTableWidget クラスのメンバ関数の一つで、指定された (row, column) の位置にあるアイテム(セル)をテーブルから取り除くために使用されます。

重要な点は、この関数がアイテムをテーブルから「取り除く」だけであり、メモリから削除するわけではないという点です。

  1. takeItem() を呼び出すと、指定したセルに設定されていた QTableWidgetItem オブジェクトへのポインタが返されます。
  2. この関数が呼ばれた後、そのアイテムは QTableWidget の管理下から外れます。つまり、テーブル上から表示されなくなります。
  3. しかし、アイテムのデータ自体はメモリ上に残っており、返されたポインタを使ってそのアイテムにアクセスしたり、別の場所に再利用したりすることが可能です。

removeItem()delete との違い

QTableWidget::takeItem() は、しばしば setItem()delete と比較されます。

  • delete item (直接アイテムを削除): QTableWidget に設定されている QTableWidgetItem を直接 delete した場合、そのアイテムはテーブルからも削除されます。ただし、これは takeItem() とは異なり、アイテムを再利用する意図がない場合に用いられます。Qtのドキュメントや慣習では、テーブルからアイテムを取り除く際には takeItem() を使用し、その後必要に応じて delete することが推奨されています。
  • takeItem(int row, int column): 指定されたセルからアイテムを取り除き、そのアイテムの所有権を呼び出し側に渡します。アイテムは削除されません。したがって、takeItem() で取り出したアイテムを不要にする場合は、手動で delete する必要があります
  • setItem(int row, int column, QTableWidgetItem *item): 指定されたセルに新しいアイテムを設定します。もしそのセルにすでにアイテムが存在する場合、既存のアイテムは自動的に削除されますQTableWidget が所有権を持つため)。

使用例

ある特定のセル(例えば0行0列)のアイテムをテーブルから一時的に取り出し、その後別のセル(例えば1行1列)に移動させるような場合に takeItem() が役立ちます。

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>

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

    QTableWidget *tableWidget = new QTableWidget(3, 3); // 3x3のテーブルを作成

    // アイテムを設定
    tableWidget->setItem(0, 0, new QTableWidgetItem("Item 0,0"));
    tableWidget->setItem(0, 1, new QTableWidgetItem("Item 0,1"));
    tableWidget->setItem(1, 0, new QTableWidgetItem("Item 1,0"));

    qDebug() << "--- Before takeItem ---";
    for (int r = 0; r < tableWidget->rowCount(); ++r) {
        for (int c = 0; c < tableWidget->columnCount(); ++c) {
            QTableWidgetItem *item = tableWidget->item(r, c);
            if (item) {
                qDebug() << "Row:" << r << ", Col:" << c << ", Text:" << item->text();
            } else {
                qDebug() << "Row:" << r << ", Col:" << c << ", Text: (nullptr)";
            }
        }
    }

    // (0,0) のアイテムを取り出す
    QTableWidgetItem *takenItem = tableWidget->takeItem(0, 0);

    qDebug() << "\n--- After takeItem(0,0) ---";
    for (int r = 0; r < tableWidget->rowCount(); ++r) {
        for (int c = 0; c < tableWidget->columnCount(); ++c) {
            QTableWidgetItem *item = tableWidget->item(r, c);
            if (item) {
                qDebug() << "Row:" << r << ", Col:" << c << ", Text:" << item->text();
            } else {
                qDebug() << "Row:" << r << ", Col:" << c << ", Text: (nullptr)";
            }
        }
    }

    if (takenItem) {
        qDebug() << "\nTaken item text:" << takenItem->text();
        // 取り出したアイテムを別の場所 (1,1) に再配置
        tableWidget->setItem(1, 1, takenItem);
    }

    qDebug() << "\n--- After setItem(1,1) with taken item ---";
    for (int r = 0; r < tableWidget->rowCount(); ++r) {
        for (int c = 0; c < tableWidget->columnCount(); ++c) {
            QTableWidgetItem *item = tableWidget->item(r, c);
            if (item) {
                qDebug() << "Row:" << r << ", Col:" << c << ", Text:" << item->text();
            } else {
                qDebug() << "Row:" << r << ", Col:" << c << ", Text: (nullptr)";
            }
        }
    }

    tableWidget->setWindowTitle("QTableWidget::takeItem() Example");
    tableWidget->resize(400, 300);
    tableWidget->show();

    return app.exec();
}


QTableWidget::takeItem() は便利ですが、その性質上、誤った使い方をすると問題を引き起こすことがあります。

メモリリーク(Memory Leak)

エラーの状況
takeItem() でアイテムを取り出した後、その QTableWidgetItemdelete し忘れたり、別の場所に再設定しなかったりすると、メモリリークが発生します。takeItem() はアイテムをテーブルから取り除くものの、そのメモリの所有権は呼び出し元に移るためです。

よくあるコード例(問題あり)

// あるセルからアイテムを取り出すが、その後何も処理しない
QTableWidgetItem *item = tableWidget->takeItem(0, 0);
// ここで item が delete されないとメモリリーク

トラブルシューティング

  • アイテムを別のセルに移動させる場合は、setItem() を使って再設定すれば、新しい親(QTableWidget)が所有権を持つため、delete は不要になります。
    QTableWidgetItem *takenItem = tableWidget->takeItem(0, 0);
    if (takenItem) {
        tableWidget->setItem(1, 1, takenItem); // ここで所有権がテーブルに戻る
    }
    
  • takeItem() で取得したポインタが不要になった場合は、必ず delete するようにしてください。
    QTableWidgetItem *item = tableWidget->takeItem(0, 0);
    if (item) { // nullptrチェックは重要
        delete item;
        item = nullptr; // dangling pointerを避ける
    }
    

無効なインデックスへのアクセス(Out-of-bounds Access)

エラーの状況
takeItem(row, column) に存在しない行や列のインデックスを渡すと、nullptr が返されることがあります。この nullptr をチェックせずにそのポインタを dereference(-> 演算子などでアクセス)しようとすると、クラッシュ(Segmentation Fault)の原因になります。

よくあるコード例(問題あり)

// 存在しないセル (99, 99) のアイテムを取り出そうとする
QTableWidgetItem *item = tableWidget->takeItem(99, 99);
item->setText("New Text"); // item が nullptr の場合、クラッシュ!

トラブルシューティング

  • rowCount()columnCount() を使用して、指定するインデックスが有効な範囲内にあることを事前に確認することも重要です。
  • takeItem() の戻り値は常に nullptr チェックする習慣をつけましょう。
    QTableWidgetItem *item = tableWidget->takeItem(row, column);
    if (item) {
        // アイテムが存在する場合の処理
        item->setText("新しいテキスト");
    } else {
        // アイテムが存在しない場合の処理 (例: エラーメッセージ表示など)
        qWarning() << "指定された位置にアイテムがありません: (" << row << "," << column << ")";
    }
    

複数の QTableWidget 間でのアイテムの移動(Ownership Transfer Issues)

エラーの状況
QTableWidget から takeItem() で取り出したアイテムを、別の QTableWidgetsetItem() で設定しようとしたときに、意図しない挙動やエラーが発生する場合があります。特に、同じアイテムを複数のテーブルに設定しようとすると、「QTableWidget: cannot insert an item that is already owned by another QTableWidget」のような警告が出ることがあります。

よくあるコード例(問題あり)

QTableWidget *table1 = new QTableWidget();
QTableWidget *table2 = new QTableWidget();

// table1 にアイテムを設定
table1->setItem(0, 0, new QTableWidgetItem("Original Item"));

// table1 からアイテムを取り出す
QTableWidgetItem *item = table1->takeItem(0, 0);

// 取り出したアイテムを table2 に設定
table2->setItem(0, 0, item);

// 間違って、同じ item を再び table1 に設定しようとする (所有権は既に table2 に移っている)
// table1->setItem(1, 1, item); // これを行うと問題が発生する可能性あり

トラブルシューティング

  • もし複数のテーブルで同じ内容を表示したい場合は、QTableWidgetItem オブジェクト自体を共有するのではなく、QTableWidgetItem の内容(テキスト、アイコンなど)をコピーして新しい QTableWidgetItem を作成し、それを別のテーブルに設定するようにします。
  • アイテムを別のテーブルに移動させる場合は、takeItem() で取り出し、その後一度だけ新しいテーブルに setItem() で設定してください。
  • QTableWidgetItem は一度に一つの QTableWidget にしか所属できません。takeItem() で取り出したアイテムは、その時点で以前のテーブルの所有権を失います。

シグナルとスロットの挙動

エラーの状況
takeItem() はアイテムをテーブルから取り除きますが、これによって itemChanged()cellChanged() といったシグナルが直接発生するわけではありません。これらのシグナルは、アイテムのデータが変更されたり、新しいアイテムが設定されたりした場合に発生します。

トラブルシューティング

  • 例えば、ある行や列を完全に削除したい場合は、removeRow()removeColumn() を使用する方が適切です。これらの関数は、その行/列内のすべてのアイテムを自動的に削除します。
  • takeItem() でアイテムを「削除」したことをUIの他の部分に通知したい場合、明示的にカスタムシグナルを発生させるか、関連するUI要素の状態を更新するコードを追加する必要があります。

QTableWidget の初期化不足

エラーの状況
QTableWidget の行数や列数を設定する前に takeItem() を呼び出そうとすると、当然ながらアイテムは存在しないため、nullptr が返ってきます。

よくあるコード例(問題あり)

QTableWidget *tableWidget = new QTableWidget(); // 初期化時に行数・列数を指定しない
// tableWidget->setRowCount(3); // これがない
// tableWidget->setColumnCount(3); // これがない

QTableWidgetItem *item = tableWidget->takeItem(0, 0); // ここで nullptr が返る
  • QTableWidget を作成する際に、コンストラクタで引数として行数と列数を指定するか、setRowCount()setColumnCount() を使って明示的に設定してください。
    // コンストラクタで指定
    QTableWidget *tableWidget = new QTableWidget(3, 3);
    
    // あるいは後から設定
    // QTableWidget *tableWidget = new QTableWidget();
    // tableWidget->setRowCount(3);
    // tableWidget->setColumnCount(3);
    


QTableWidget::takeItem(int row, int column) は、指定されたセルから QTableWidgetItem を取り出し、その所有権を呼び出し元に移します。この特性を利用した様々なプログラミング例を見ていきましょう。

例1: アイテムの移動 (あるセルから別のセルへ)

最も一般的な使用例は、テーブル内のアイテムをある場所から別の場所に移動させる場合です。

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QDebug> // デバッグ出力用

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

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

    QTableWidget *tableWidget = new QTableWidget(3, 3); // 3x3のテーブルを作成
    layout->addWidget(tableWidget);

    // 初期アイテムを設定
    tableWidget->setItem(0, 0, new QTableWidgetItem("元の位置 (0,0)"));
    tableWidget->setItem(1, 0, new QTableWidgetItem("他のアイテム (1,0)"));
    tableWidget->setItem(2, 2, new QTableWidgetItem("別のアイテム (2,2)"));

    QPushButton *moveButton = new QPushButton("アイテムを移動 (0,0) -> (1,2)");
    layout->addWidget(moveButton);

    QObject::connect(moveButton, &QPushButton::clicked, [&]() {
        // (0,0) からアイテムを取り出す
        QTableWidgetItem *itemToMove = tableWidget->takeItem(0, 0);

        if (itemToMove) { // アイテムが存在することを確認
            qDebug() << "アイテムを取り出しました: " << itemToMove->text();
            // 取り出したアイテムを (1,2) に設定
            tableWidget->setItem(1, 2, itemToMove);
            qDebug() << "(0,0) のアイテムは取り除かれ、(1,2) に移動しました。";
        } else {
            qDebug() << "(0,0) に移動するアイテムがありませんでした。";
        }

        // 移動後のテーブルの状態を出力
        qDebug() << "\n--- 現在のテーブルの状態 ---";
        for (int r = 0; r < tableWidget->rowCount(); ++r) {
            for (int c = 0; c < tableWidget->columnCount(); ++c) {
                QTableWidgetItem *item = tableWidget->item(r, c);
                if (item) {
                    qDebug() << "Row:" << r << ", Col:" << c << ", Text:" << item->text();
                } else {
                    qDebug() << "Row:" << r << ", Col:" << c << ", Text: (空)";
                }
            }
        }
    });

    window->setWindowTitle("QTableWidget::takeItem() アイテム移動の例");
    window->resize(500, 300);
    window->show();

    return app.exec();
}

解説

  1. tableWidget->takeItem(0, 0) を呼び出すと、(0,0) のセルから QTableWidgetItem が取り除かれ、そのポインタが itemToMove に格納されます。この時点で、テーブルのそのセルは空になります。
  2. itemToMovenullptr でないことを確認した後、tableWidget->setItem(1, 2, itemToMove) を呼び出すことで、同じアイテムを (1,2) のセルに再設定します。これにより、アイテムの所有権が再び QTableWidget に戻ります。

例2: 選択されたアイテムの一括削除

takeItem() を使用して、ユーザーが選択した複数のアイテムをテーブルから削除する例です。

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>
#include <QList>

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

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

    QTableWidget *tableWidget = new QTableWidget(5, 2); // 5x2のテーブルを作成
    layout->addWidget(tableWidget);

    // 初期アイテムを設定
    for (int r = 0; r < tableWidget->rowCount(); ++r) {
        for (int c = 0; c < tableWidget->columnCount(); ++c) {
            tableWidget->setItem(r, c, new QTableWidgetItem(QString("セル %1,%2").arg(r).arg(c)));
        }
    }

    QPushButton *deleteSelectedButton = new QPushButton("選択されたアイテムを削除");
    layout->addWidget(deleteSelectedButton);

    QObject::connect(deleteSelectedButton, &QPushButton::clicked, [&]() {
        // 選択されたアイテムのリストを取得 (QTableWidgetItemへのポインタのリスト)
        QList<QTableWidgetItem*> selectedItems = tableWidget->selectedItems();

        if (selectedItems.isEmpty()) {
            qDebug() << "削除するアイテムが選択されていません。";
            return;
        }

        qDebug() << "選択されたアイテムを削除します:";

        // set<QPair<int, int>> を使用して重複するセルを避ける
        QSet<QPair<int, int>> processedCells;

        for (QTableWidgetItem *item : selectedItems) {
            if (item) {
                int row = item->row();
                int col = item->column();
                QPair<int, int> cellCoord = qMakePair(row, col);

                // 既に処理済みのセルでなければ、takeItemを呼び出す
                if (!processedCells.contains(cellCoord)) {
                    QTableWidgetItem *takenItem = tableWidget->takeItem(row, col);
                    if (takenItem) {
                        qDebug() << "  削除: セル (" << row << "," << col << ") - テキスト: " << takenItem->text();
                        delete takenItem; // メモリリークを防ぐために削除
                        takenItem = nullptr;
                    }
                    processedCells.insert(cellCoord); // 処理済みとしてマーク
                }
            }
        }
        qDebug() << "削除が完了しました。";
    });

    window->setWindowTitle("QTableWidget::takeItem() 選択アイテム削除の例");
    window->resize(500, 400);
    window->show();

    return app.exec();
}

解説

  1. tableWidget->selectedItems() で現在選択されているすべての QTableWidgetItem のリストを取得します。
  2. リストをイテレートし、各アイテムの行と列を取得します。
  3. tableWidget->takeItem(row, column) を呼び出して、そのアイテムをテーブルから取り除きます。
  4. 取り出したアイテム (takenItem) はもう不要なので、delete takenItem; でメモリから解放します。これを忘れるとメモリリークになります。
  5. QSet<QPair<int, int>> を使用して、選択されたアイテムが同じセルを指す場合でも、takeItem() が二重に呼ばれないように重複をチェックしています。これは、同じセルが複数回選択される可能性がある場合(例えば、ユーザーがShiftキーを押しながら複数のセルを選択した場合など)に特に重要です。

例3: アイテムの「抽出」とリスト表示

テーブルから特定の条件に合うアイテムを抽出し、別の QListWidget などで表示する例です。

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QDebug>
#include <QSet>

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

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

    QTableWidget *tableWidget = new QTableWidget(4, 2); // 4x2のテーブル
    layout->addWidget(tableWidget);

    // 初期アイテムを設定
    tableWidget->setItem(0, 0, new QTableWidgetItem("りんご"));
    tableWidget->setItem(0, 1, new QTableWidgetItem("果物"));
    tableWidget->setItem(1, 0, new QTableWidgetItem("にんじん"));
    tableWidget->setItem(1, 1, new QTableWidgetItem("野菜"));
    tableWidget->setItem(2, 0, new QTableWidgetItem("バナナ"));
    tableWidget->setItem(2, 1, new QTableWidgetItem("果物"));
    tableWidget->setItem(3, 0, new QTableWidgetItem("ブロッコリー"));
    tableWidget->setItem(3, 1, new QTableWidgetItem("野菜"));

    QPushButton *extractButton = new QPushButton("「果物」を抽出");
    layout->addWidget(extractButton);

    QLabel *extractedLabel = new QLabel("抽出された果物:");
    layout->addWidget(extractedLabel);

    QListWidget *extractedList = new QListWidget();
    layout->addWidget(extractedList);

    QObject::connect(extractButton, &QPushButton::clicked, [&]() {
        extractedList->clear(); // 既存のリストをクリア

        // 逆順でループすると、行を削除してもインデックスが狂わない
        for (int r = tableWidget->rowCount() - 1; r >= 0; --r) {
            QTableWidgetItem *categoryItem = tableWidget->item(r, 1); // 2列目 (カテゴリ) のアイテム

            if (categoryItem && categoryItem->text() == "果物") {
                QTableWidgetItem *fruitNameItem = tableWidget->takeItem(r, 0); // 果物名を取り出す
                // categoryItem も取り除く
                QTableWidgetItem *takenCategoryItem = tableWidget->takeItem(r, 1);

                if (fruitNameItem) {
                    extractedList->addItem(fruitNameItem->text()); // リストウィジェットに追加
                    delete fruitNameItem; // QListWidgetItemはテキストのコピーを作成するので、元のアイテムは削除
                    fruitNameItem = nullptr;
                }
                if (takenCategoryItem) {
                    delete takenCategoryItem; // 不要になったカテゴリアイテムも削除
                    takenCategoryItem = nullptr;
                }
            }
        }
        qDebug() << "「果物」の抽出が完了しました。";
    });

    window->setWindowTitle("QTableWidget::takeItem() アイテム抽出の例");
    window->resize(600, 400);
    window->show();

    return app.exec();
}
  1. この例では、テーブルの2列目(インデックス1)のテキストが「果物」である行を特定します。
  2. 該当する行が見つかったら、その行の1列目(果物名)のアイテムと2列目(カテゴリ名)のアイテムを takeItem() で取り出します。
  3. 取り出した果物名のテキストを QListWidget に追加します。QListWidget::addItem() は文字列を受け取ると新しい QListWidgetItem を内部で作成するため、元の fruitNameItem は不要になり delete します。
  4. カテゴリのアイテムもテーブルから取り除かれるので、不要であれば delete します。
  5. テーブルからアイテムを取り除く際に、ループのインデックスが狂わないように逆順 ( r = tableWidget->rowCount() - 1; r >= 0; --r ) でループしている点に注目してください。行や列を削除する場合、順方向でループするとインデックスがずれてしまい、エラーの原因になることがあります。


Qtプログラミングにおいて QTableWidget::takeItem() はアイテムをテーブルから取り除き、所有権を呼び出し元に移すという特定の目的で使用されます。しかし、この機能が最適ではない状況や、別の目的でセルを操作したい場合に、いくつかの代替手段が存在します。

QTableWidget::takeItem() はアイテムを「引き抜く」際に使いますが、目的によっては以下の方法の方が適切です。

セルの内容を消去する (setItem(row, col, nullptr))

アイテムを完全に削除したいが、行や列は残したい場合に最も簡単な方法です。nullptr を設定することで、そのセルのアイテムは QTableWidget によって自動的に削除されます。

  • デメリット
    アイテムのデータを再利用できない。
  • メリット
    シンプルで簡単。メモリ管理を QTableWidget に任せられる。
  • 目的
    セルの内容を空にする。アイテムのデータは不要。

コード例

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>

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

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

    QTableWidget *tableWidget = new QTableWidget(3, 3);
    layout->addWidget(tableWidget);

    tableWidget->setItem(0, 0, new QTableWidgetItem("元のアイテム (0,0)"));
    tableWidget->setItem(0, 1, new QTableWidgetItem("別のアイテム (0,1)"));

    QPushButton *clearButton = new QPushButton("セル(0,0)の内容を消去");
    layout->addWidget(clearButton);

    QObject::connect(clearButton, &QPushButton::clicked, [&]() {
        // (0,0) のアイテムを nullptr で置き換えることで削除
        tableWidget->setItem(0, 0, nullptr);
        qDebug() << "(0,0) のアイテムが消去されました。";

        // 消去後の状態を確認
        if (tableWidget->item(0, 0) == nullptr) {
            qDebug() << "(0,0) は現在空です。";
        }
    });

    window->setWindowTitle("setItem(nullptr) の例");
    window->resize(400, 200);
    window->show();

    return app.exec();
}

行または列を削除する (removeRow(), removeColumn())

特定の行全体、または列全体をテーブルから削除したい場合に、これらの関数を使用します。これにより、その行/列に含まれるすべてのアイテムは自動的に削除されます。

  • デメリット
    削除されたアイテムのデータを再利用できない。
  • メリット
    行/列内の全アイテムの削除と、テーブル構造の再編成が一度に行われる。
  • 目的
    行または列全体を削除する。

コード例

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>

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

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

    QTableWidget *tableWidget = new QTableWidget(3, 3);
    layout->addWidget(tableWidget);

    for (int r = 0; r < tableWidget->rowCount(); ++r) {
        for (int c = 0; c < tableWidget->columnCount(); ++c) {
            tableWidget->setItem(r, c, new QTableWidgetItem(QString("セル %1,%2").arg(r).arg(c)));
        }
    }

    QPushButton *removeRowButton = new QPushButton("1行目を削除");
    layout->addWidget(removeRowButton);

    QObject::connect(removeRowButton, &QPushButton::clicked, [&]() {
        if (tableWidget->rowCount() > 1) {
            tableWidget->removeRow(1); // 1行目 (2番目の行) を削除
            qDebug() << "1行目 (インデックス1) を削除しました。";
        } else {
            qDebug() << "これ以上削除する行がありません。";
        }
    });

    window->setWindowTitle("removeRow() の例");
    window->resize(400, 200);
    window->show();

    return app.exec();
}

アイテムの表示を隠す (setHidden())

アイテムを一時的に見えなくしたいだけで、削除したり所有権を移したりしたくない場合に利用できます。

  • デメリット
    テーブルのレイアウト(行の高さや列の幅)は変化しない場合がある。見かけ上消えただけで、データは残っている。
  • メリット
    アイテムのデータを保持したまま、必要に応じて表示・非表示を切り替えられる。
  • 目的
    セルを非表示にする。データは保持したい。

コード例

#include <QApplication>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>

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

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

    QTableWidget *tableWidget = new QTableWidget(3, 3);
    layout->addWidget(tableWidget);

    tableWidget->setItem(0, 0, new QTableWidgetItem("表示/非表示を切り替えるアイテム"));
    tableWidget->setItem(0, 1, new QTableWidgetItem("常に表示されるアイテム"));

    QPushButton *toggleHiddenButton = new QPushButton("セル(0,0)の表示/非表示を切り替え");
    layout->addWidget(toggleHiddenButton);

    QObject::connect(toggleHiddenButton, &QPushButton::clicked, [&]() {
        QTableWidgetItem *item = tableWidget->item(0, 0);
        if (item) {
            bool isHidden = item->isHidden();
            item->setHidden(!isHidden); // 表示/非表示を切り替える
            qDebug() << "(0,0) のアイテムの表示状態が" << (isHidden ? "表示" : "非表示") << "に切り替わりました。";
        }
    });

    window->setWindowTitle("setHidden() の例");
    window->resize(400, 200);
    window->show();

    return app.exec();
}

アイテムのフラグを変更する (setFlags())

アイテムを非表示にするのではなく、選択不可や編集不可にするなど、ユーザーインタラクションを制御したい場合に利用します。これは直接的な「削除」ではありませんが、ユーザーから見えなくしたり、操作させないようにしたりする点で関連性があります。

  • デメリット
    見た目は変わらない場合が多い。
  • メリット
    アイテムの存在を保持しつつ、特定の操作を制限できる。
  • 目的
    セルのインタラクション(選択、編集など)を制御する。

コード例(省略)
QTableWidgetItemsetFlags() メソッドを使用して Qt::ItemIsEnabled, Qt::ItemIsSelectable, Qt::ItemIsEditable などのフラグを設定・解除します。例えば、item->setFlags(item->flags() & ~Qt::ItemIsEditable); で編集不可にできます。

モデル/ビューアーキテクチャ (QAbstractTableModel を継承する)

QTableWidgetQAbstractTableModel を継承した高レベルなウィジェットですが、より複雑なデータ管理やパフォーマンスが求められる場合は、カスタムモデル(QAbstractTableModel を継承したクラス)を作成し、それを QTableView に設定する「モデル/ビューアーキテクチャ」が推奨されます。

このアプローチでは、データの追加、削除、変更はモデル内のデータ構造を直接操作することで行われ、QTableView はモデルの変更通知(beginRemoveRows(), endRemoveRows() など)を受け取ってビューを更新します。

  • デメリット
    QTableWidget よりも学習コストと実装の手間がかかる。
  • メリット
    データと表示が分離され、柔軟性と拡張性が高い。データの追加/削除/変更が効率的に行える。
  • 目的
    大量のデータ、複雑なデータ構造、パフォーマンスが要求される場合に最適。
  1. QAbstractTableModel を継承したカスタムモデルクラスを作成します。
  2. rowCount(), columnCount(), data(), headerData(), setData(), flags() などの仮想関数をオーバーライドして、データを提供・操作するロジックを記述します。
  3. データの削除は、モデル内のデータ構造(例:QList<QStringList>std::vector<MyCustomData>) から直接アイテムを削除し、その後 beginRemoveRows() / endRemoveRows() (または beginRemoveColumns() / endRemoveColumns()) を呼び出すことでビューに通知します。
  4. QTableView オブジェクトを作成し、tableView->setModel(myCustomModel); でモデルを設定します。