QTableWidget::row() エラーとトラブルシューティング:Qt アプリ開発のヒント

2025-05-27

QTableWidget::row()」は、QtフレームワークのQTableWidgetクラスで提供されるpublicなconstメソッドです。このメソッドは、QTableWidget内で現在選択されているアイテムが存在する行のインデックスを返します。

より具体的に説明すると、

  • 選択されていない場合
    もしテーブル内で何も選択されていない場合、または選択されているアイテムが無効な場合(通常はありえませんが)、このメソッドは**-1**を返します。
  • 戻り値
    選択されているアイテムが存在する行の0から始まるインデックス(整数値)を返します。
  • 役割
    現在ユーザーが選択しているセル(アイテム)がある行番号を取得するために使われます。


もし、QTableWidgetの3行目(インデックスは2)にあるセルが選択されている状態で、row()メソッドを呼び出すと、戻り値は2になります。

使い方のイメージ

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

// ... テーブルにアイテムを追加する処理 ...

QItemSelectionModel *selectionModel = tableWidget->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();

if (!selectedIndexes.isEmpty()) {
    // 少なくとも1つのアイテムが選択されている場合
    int selectedRow = tableWidget->row(selectedIndexes.first());
    qDebug() << "選択された行のインデックス:" << selectedRow;
} else {
    qDebug() << "何も選択されていません。";
}

上記の例では、まず選択されているアイテムのインデックスリストを取得し、リストが空でなければ最初の選択されたアイテムのインデックスを使ってrow()メソッドを呼び出し、選択された行のインデックスを表示しています。



  1. 何も選択されていない場合の誤解

    • エラー
      何もセルが選択されていない状態で row() を呼び出すと -1 が返ります。これを有効な行インデックスと誤って扱い、配列やリストの範囲外アクセスなどのエラーを引き起こすことがあります。
    • トラブルシューティング
      row() の戻り値が -1 でないことを確認してから、その行インデックスを使った処理を行うようにします。選択されているかどうかを事前に QTableWidget::selectionModel()->hasSelection() などでチェックするのが安全です。

    <!-- end list -->

    int selectedRow = tableWidget->row(tableWidget->currentItem());
    if (selectedRow >= 0) {
        // 選択されている行に対する処理
        qDebug() << "選択された行:" << selectedRow;
    } else {
        qDebug() << "何も選択されていません。";
    }
    
  2. currentItem() がnullptrの場合

    • エラー
      QTableWidget::currentItem() は現在フォーカスを持っているアイテム(必ずしも選択されているとは限りません)を返します。もしテーブルにアイテムが一つもない場合や、フォーカスが当たっているアイテムがない場合、currentItem()nullptr を返す可能性があります。これを直接 row() に渡すと、不正なメモリアクセスを引き起こす可能性があります。
    • トラブルシューティング
      currentItem() の戻り値が nullptr でないことを確認してから row() を呼び出すようにします。
    QTableWidgetItem *currentItem = tableWidget->currentItem();
    if (currentItem) {
        int selectedRow = tableWidget->row(currentItem);
        qDebug() << "現在のアイテムの行:" << selectedRow;
    } else {
        qDebug() << "現在のアイテムはありません。";
    }
    
  3. 選択モデルとの連携の誤解

    • エラー
      QTableWidget::row() は、引数として QTableWidgetItem* を直接取るオーバーロードはありません。引数を指定する場合は、QModelIndex を取る必要があります。QTableWidget::selectedIndexes() で得られる QModelIndexList の最初の要素などを渡す必要があります。QTableWidget::currentItem()QTableWidgetItem* を返すため、直接 row() に渡すことができます。
    • トラブルシューティング
      選択されたアイテムの行を取得したい場合は、QTableWidget::selectionModel()->selectedIndexes() を使用して QModelIndexList を取得し、その最初の要素(または適切な要素)に対して QModelIndex::row() を呼び出すか、QTableWidget::row(const QModelIndex &index) を使用します。現在のアイテムの行を取得したい場合は、QTableWidget::currentItem() を使用し、そのポインタを引数なしの QTableWidget::row() に渡します。
    QItemSelectionModel *selectionModel = tableWidget->selectionModel();
    QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
    if (!selectedIndexes.isEmpty()) {
        int selectedRow = selectedIndexes.first().row(); // QModelIndex::row() を使用
        qDebug() << "選択された行 (モデルインデックスから):" << selectedRow;
    }
    
    QTableWidgetItem *currentItem = tableWidget->currentItem();
    if (currentItem) {
        int currentRow = tableWidget->row(currentItem); // QTableWidgetItem* を引数に取るrow() は存在しません
        // 正しくは引数なしの row() をcurrentItem() と組み合わせて使うか、
        // currentItem() から QModelIndex を取得して row() を呼び出す
        int currentRowViaIndex = tableWidget->indexFromItem(currentItem).row();
        qDebug() << "現在のアイテムの行 (indexFromItem):" << currentRowViaIndex;
    }
    
  4. シグナル・スロットとの連携の誤解

    • エラー
      特定の行が選択されたときに何か処理を行いたい場合、QTableWidget::currentRowChanged(int) などのシグナルを使用するのが一般的です。row() を手動で呼び出すタイミングによっては、意図した行とは異なる行のインデックスを取得してしまうことがあります。
    • トラブルシューティング
      行選択の変化に反応する場合は、適切なシグナル (currentRowChanged, itemSelectionChanged など) をスロットに接続し、スロット内で現在の行インデックスを取得するようにします。
    // ヘッダーファイルでスロットを宣言
    private slots:
        void onCurrentRowChanged(int currentRow);
    
    // ソースファイルでスロットを実装
    void MyWidget::onCurrentRowChanged(int currentRow)
    {
        qDebug() << "現在の行が変更されました:" << currentRow;
        // currentRow を使った処理
    }
    
    // コンストラクタなどでシグナルとスロットを接続
    connect(tableWidget, &QTableWidget::currentRowChanged, this, &MyWidget::onCurrentRowChanged);
    
  5. 行の追加・削除時のインデックスのずれ

    • エラー
      行を追加または削除した後に、以前に取得した行インデックスが正しくなくなる可能性があります。
    • トラブルシューティング
      行インデックスを永続的に保存するのではなく、必要なときに最新の選択状態に基づいて row() を呼び出すようにします。もし特定のアイテムに関連する行を追跡する必要がある場合は、アイテムへのポインタを保持するなどの方法を検討します。


#include <QApplication>
#include <QTableWidget>
#include <QDebug>
#include <QItemSelectionModel>

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

    QTableWidget tableWidget(5, 3); // 5行3列のテーブルを作成
    tableWidget.setWindowTitle("QTableWidget::row() の例");

    // テーブルにいくつかのアイテムを追加
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 3; ++j) {
            tableWidget.setItem(i, j, new QTableWidgetItem(QString("セル %1-%2").arg(i).arg(j)));
        }
    }

    // 選択モデルを取得
    QItemSelectionModel *selectionModel = tableWidget.selectionModel();

    // 選択が変更されたときのシグナルに接続
    QObject::connect(selectionModel, &QItemSelectionModel::selectionChanged,
                     [&](const QItemSelection &selected, const QItemSelection &deselected) {
        if (!selected.isEmpty()) {
            // 選択された最初のアイテムのインデックスを取得
            QModelIndex firstSelectedIndex = selected.indexes().first();
            int selectedRow = firstSelectedIndex.row();
            qDebug() << "選択された行のインデックス:" << selectedRow;
        }
    });

    tableWidget.show();

    return a.exec();
}

説明

  1. QTableWidget を作成し、いくつかのアイテムを追加しています。
  2. tableWidget.selectionModel() で選択モデルを取得します。
  3. QItemSelectionModel::selectionChanged シグナルにラムダ関数を接続しています。このシグナルは、テーブル内の選択状態が変更されるたびに発行されます。
  4. ラムダ関数内では、selected なったアイテムのリスト (selected.indexes()) が空でないかを確認します。
  5. 最初の選択されたアイテムの QModelIndex を取得し、その row() メソッドを呼び出して行インデックスを取得しています。
  6. 取得した行インデックスを qDebug() で出力します。

この例では、テーブル内で現在のアイテム(フォーカスがあるアイテム)が変更されたときに、その行インデックスを取得して処理を行います。

#include <QApplication>
#include <QTableWidget>
#include <QDebug>

class MyTableWidget : public QTableWidget
{
public:
    MyTableWidget(int rows, int columns, QWidget *parent = nullptr)
        : QTableWidget(rows, columns, parent)
    {
        connect(this, &QTableWidget::currentItemChanged, this, &MyTableWidget::onCurrentItemChanged);
    }

private slots:
    void onCurrentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
    {
        if (current) {
            int currentRow = row(current); // 引数に QTableWidgetItem* を渡す row()
            qDebug() << "現在のアイテムがある行のインデックス:" << currentRow;
            // ここで現在の行に対する何らかの処理を行うことができます
        }
    }
};

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

    MyTableWidget tableWidget(5, 3);
    tableWidget.setWindowTitle("QTableWidget::row(QTableWidgetItem*) の例");

    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 3; ++j) {
            tableWidget.setItem(i, j, new QTableWidgetItem(QString("セル %1-%2").arg(i).arg(j)));
        }
    }

    tableWidget.show();

    return a.exec();
}

説明

  1. QTableWidget を継承した MyTableWidget クラスを作成しています。
  2. コンストラクタで QTableWidget::currentItemChanged シグナルを、自身の onCurrentItemChanged スロットに接続しています。このシグナルは、現在のアイテムが変更されるたびに発行されます。
  3. onCurrentItemChanged スロットでは、引数として渡される現在のアイテム (current) が nullptr でないかを確認します。
  4. row(current) を呼び出して、現在のアイテムが存在する行のインデックスを取得します。
  5. 取得した行インデックスを qDebug() で出力し、コメントで「ここで現在の行に対する何らかの処理を行うことができます」と示しています。
  • QTableWidget::row() に引数として QTableWidgetItem* を渡すオーバーロードは存在しません。上記の例は、現在のアイテム (currentItem()) を取得し、引数なしの row() メソッドと組み合わせて使うか、indexFromItem()QModelIndex を取得してから row() を呼び出すのが正しい方法です。上記の例は、意図的に誤った使い方を示し、注意を促す意図があります。

正しい例

    void onCurrentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
    {
        if (current) {
            int currentRow = indexFromItem(current).row(); // 正しい方法: indexFromItem() を使用
            qDebug() << "現在のアイテムがある行のインデックス (正しい方法):" << currentRow;
        }
    }

この例では、特定の QTableWidgetItem オブジェクトがわかっている場合に、そのアイテムが存在する行のインデックスを取得する方法を示します。

#include <QApplication>
#include <QTableWidget>
#include <QDebug>

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

    QTableWidget tableWidget(5, 3);
    tableWidget.setWindowTitle("特定のアイテムの行インデックス");

    QTableWidgetItem *targetItem = nullptr;

    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 3; ++j) {
            QTableWidgetItem *item = new QTableWidgetItem(QString("セル %1-%2").arg(i).arg(j));
            tableWidget.setItem(i, j, item);
            if (i == 2 && j == 1) {
                targetItem = item; // 特定のアイテムを保存
            }
        }
    }

    if (targetItem) {
        int targetRow = tableWidget->row(tableWidget->indexFromItem(targetItem));
        qDebug() << "ターゲットアイテムがある行のインデックス:" << targetRow;
    } else {
        qDebug() << "ターゲットアイテムが見つかりませんでした。";
    }

    tableWidget.show();

    return a.exec();
}
  1. テーブルを作成し、アイテムを追加する際に、特定の条件(ここでは 3行目、2列目のアイテム)を満たすアイテムへのポインタを targetItem に保存します。
  2. targetItemnullptr でないことを確認した後、tableWidget->indexFromItem(targetItem) を呼び出して、そのアイテムに対応する QModelIndex を取得します。
  3. 取得した QModelIndex に対して row() メソッドを呼び出すことで、アイテムが存在する行のインデックスを取得できます。


QModelIndex::row() を直接使用する

QTableWidget は内部的にモデル(通常は QStandardItemModel)を使用しています。選択されたアイテムや特定のアイテムに対応する QModelIndex オブジェクトを取得できれば、その QModelIndex オブジェクトの row() メソッドを呼び出すことで行インデックスを取得できます。

QItemSelectionModel *selectionModel = tableWidget->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();

if (!selectedIndexes.isEmpty()) {
    QModelIndex firstSelectedIndex = selectedIndexes.first();
    int selectedRow = firstSelectedIndex.row(); // QModelIndex の row() を使用
    qDebug() << "選択された行 (QModelIndex):" << selectedRow;
}

QTableWidgetItem *item = tableWidget->item(2, 1); // 特定のアイテムを取得
if (item) {
    QModelIndex itemIndex = tableWidget->indexFromItem(item);
    int itemRow = itemIndex.row(); // QModelIndex の row() を使用
    qDebug() << "アイテムがある行 (QModelIndex):" << itemRow;
}

利点

  • QModelIndex は行と列の両方の情報を持っているので、必要に応じて列インデックス (column()) も簡単に取得できます。
  • よりモデル/ビューの概念に沿った方法です。

欠点

  • QTableWidgetItem から直接行インデックスを取得する場合は、一度 QModelIndex に変換する手間が必要です (indexFromItem())。

シグナルを利用する

特定の行が選択されたり、現在の行が変更されたりするタイミングで処理を行いたい場合は、QTableWidget が提供するシグナルを利用できます。

  • itemSelectionChanged()
    選択状態が変更されたときに発行されます。このシグナルを受け取るスロット内で、selectionModel()->selectedIndexes() を使用して選択されたアイテムの QModelIndexList を取得し、それぞれの QModelIndex から行インデックスを取得できます。

    connect(tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
            [&]() {
        QModelIndexList selectedIndexes = tableWidget->selectionModel()->selectedIndexes();
        for (const QModelIndex &index : selectedIndexes) {
            qDebug() << "選択された行 (シグナル内):" << index.row();
        }
    });
    
  • currentRowChanged(int currentRow)
    現在の行が変更されたときに発行されます。このシグナルを受け取るスロットでは、変更後の行インデックスが直接渡されます。

    connect(tableWidget, &QTableWidget::currentRowChanged,
            [](int currentRow) {
        qDebug() << "現在の行が変更されました:" << currentRow;
        // currentRow を使った処理
    });
    

利点

  • 手動でポーリングする必要がありません。
  • イベント駆動型なので、特定の操作が発生したときに自動的に処理を実行できます。

欠点