Qt QListWidget: currentItem() の使い方とよくある落とし穴
QListWidget::currentItem() とは何か
QListWidget::currentItem()
は、QListWidget
ウィジェット内でユーザーが選択している(クリックまたはキーボードでハイライト表示されている)QListWidgetItem
オブジェクトへのポインタを返す関数です。
戻り値
- 何も選択されていない場合:
nullptr
(ヌルポインタ) - 現在選択されているアイテムがある場合:選択されている
QListWidgetItem
オブジェクトへのポインタ(QListWidgetItem*
)
なぜこれが重要なのか
この関数は、ユーザーがリストウィジェットで何を選択しているかをプログラムが把握し、それに基づいて何らかの処理を行うために不可欠です。例えば、以下のようなシナリオでよく使われます。
- 選択されたアイテムのテキストを取得する
ユーザーが選択したリストアイテムのテキストを、他のウィジェットに表示したり、ファイルに保存したりする場合。 - 選択されたアイテムのデータを取得する
各QListWidgetItem
には、setData()
関数でカスタムデータを関連付けることができます。currentItem()
を使ってアイテムを取得し、data()
関数でそのカスタムデータにアクセスします。 - 選択されたアイテムの属性を変更する
選択されたアイテムのフォント、色、アイコンなどを動的に変更する場合。 - 選択されたアイテムを削除する/編集する
ユーザーが「削除」ボタンを押したときに、currentItem()
で取得したアイテムをリストから削除したり、その内容を編集するためのダイアログを開いたりする場合。
以下に、QListWidget::currentItem()
の基本的な使用例を示します。
#include <QApplication>
#include <QListWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QWidget>
#include <QDebug> // デバッグ出力用
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget window;
QVBoxLayout *layout = new QVBoxLayout(&window);
QListWidget *listWidget = new QListWidget();
listWidget->addItem("アイテム 1");
listWidget->addItem("アイテム 2");
listWidget->addItem("アイテム 3");
listWidget->addItem("アイテム 4");
QLabel *selectedItemLabel = new QLabel("選択中のアイテム: なし");
selectedItemLabel->setStyleSheet("font-weight: bold;");
QPushButton *checkSelectionButton = new QPushButton("選択中のアイテムを確認");
// 選択が変更されたときに呼ばれるスロット
QObject::connect(listWidget, &QListWidget::currentItemChanged,
[=](QListWidgetItem *current, QListWidgetItem *previous) {
Q_UNUSED(previous); // previous は今回は使用しない
if (current) {
selectedItemLabel->setText("選択中のアイテム: " + current->text());
qDebug() << "新しい選択: " << current->text();
} else {
selectedItemLabel->setText("選択中のアイテム: なし");
qDebug() << "選択解除";
}
});
// ボタンがクリックされたときに呼ばれるスロット
QObject::connect(checkSelectionButton, &QPushButton::clicked,
[=]() {
QListWidgetItem *currentItem = listWidget->currentItem(); // ここで currentItem() を使用
if (currentItem) {
qDebug() << "ボタンクリック: 現在選択されているアイテムは " << currentItem->text();
// 例: 選択されたアイテムの背景色を変更
currentItem->setBackground(Qt::yellow);
} else {
qDebug() << "ボタンクリック: 何も選択されていません。";
}
});
layout->addWidget(listWidget);
layout->addWidget(selectedItemLabel);
layout->addWidget(checkSelectionButton);
window.setWindowTitle("QListWidget::currentItem() の例");
window.show();
return a.exec();
}
この例では、以下のことを行っています。
QListWidget
を作成し、いくつかアイテムを追加します。QListWidget::currentItemChanged
シグナルをlambda
関数に接続し、選択が変更されるたびにQLabel
を更新します。このシグナルが発火すると、引数として新しい選択アイテムのポインタが渡されますが、ここではcurrentItem()
の使用例を示すために、ボタンのクリックイベントでもcurrentItem()
を呼び出しています。- 「選択中のアイテムを確認」ボタンを作成し、その
clicked
シグナルをlambda
関数に接続します。 - この
lambda
関数内でlistWidget->currentItem()
を呼び出し、現在選択されているアイテムへのポインタを取得しています。 - 取得したポインタが
nullptr
でないことを確認し、選択されているアイテムのテキストをデバッグ出力しています。さらに、選択されたアイテムの背景色を変更する例も示しています。
nullptr(ヌルポインタ)が返される
エラーの原因
これは最も一般的な問題です。currentItem()
は、リストウィジェット内に何もアイテムが選択されていない場合に nullptr
を返します。
シナリオ
- リストウィジェットから全てのアイテムが削除された場合。
- プログラム的に選択を解除した場合(例:
clearSelection()
を呼び出した後)。 - ユーザーがすべてのアイテムの選択を解除した場合。
- アプリケーション起動時:リストウィジェットがまだ空の場合や、初期状態で何も選択されていない場合。
トラブルシューティング
currentItem()
の戻り値を使用する前に、必ず nullptr
チェックを行うことが非常に重要です。
QListWidgetItem *currentItem = listWidget->currentItem();
if (currentItem) {
// アイテムが選択されている場合の処理
QString text = currentItem->text();
qDebug() << "選択されたアイテム: " << text;
} else {
// 何も選択されていない場合の処理
qDebug() << "何もアイテムが選択されていません。";
// 例: エラーメッセージを表示したり、関連するUI要素を無効化したりする
}
想定と異なるアイテムが返される (特に複数選択モードで)
エラーの原因
currentItem()
は「現在キーボードフォーカスを持っているアイテム」または「最後にクリックされたアイテム」を返します。これは「選択されているアイテムのリスト」とは異なります。特に QListWidget::ExtendedSelection
や QListWidget::MultiSelection
のような複数選択モードを使用している場合、複数のアイテムが選択されているにもかかわらず、currentItem()
はそのうちの1つしか返しません。
シナリオ
- プログラム的に複数のアイテムを選択したが、
currentItem()
の値が期待と異なる。 - ユーザーがCtrlキーを押しながら複数のアイテムを選択したが、
currentItem()
はその中の1つ(通常は最後に操作した、またはキーボードフォーカスがあるアイテム)しか返さない。
トラブルシューティング
複数の選択されたアイテムを処理する必要がある場合は、currentItem()
ではなく QListWidget::selectedItems()
を使用します。これは、現在選択されているすべての QListWidgetItem
オブジェクトのリスト(QList<QListWidgetItem*>
)を返します。
QList<QListWidgetItem*> selectedItems = listWidget->selectedItems();
if (!selectedItems.isEmpty()) {
qDebug() << "選択されているアイテム:";
for (QListWidgetItem *item : selectedItems) {
qDebug() << " - " << item->text();
}
} else {
qDebug() << "何も選択されていません。";
}
シグナルとスロットのタイミングの問題
エラーの原因
QListWidget
のシグナル(例: itemClicked
, itemSelectionChanged
, currentItemChanged
)に接続されたスロット内で currentItem()
を呼び出す場合、シグナルの発火タイミングと currentItem()
の値が期待と異なることがあります。
シナリオ
currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
シグナル:このシグナルは、キーボードフォーカスを持つアイテムが変更されたときに発火します。current
引数は常に新しい現在のアイテムを指すため、このシグナルの中でcurrentItem()
を呼び出すことは冗長であり、引数current
を直接使用すべきです。itemSelectionChanged()
シグナル:このシグナルは選択状態が変更されたときに発火しますが、currentItem()
が指すアイテムはまだ古いアイテムのままだったり、複数の選択があった場合に期待するアイテムでなかったりすることがあります。itemClicked(QListWidgetItem*)
シグナル:クリックされたアイテムを直接引数で受け取れるため、通常はcurrentItem()
を使う必要はありません。むしろ、このシグナルが発火した時点では、currentItem()
がまだ更新されていない可能性や、クリックされたアイテムが「現在のアイテム」ではない(複数選択モードなど)場合があります。
トラブルシューティング
- シグナルの引数を確認する
多くのシグナルは、変更されたアイテムへのポインタを引数として提供しています。これらの引数を直接使用することで、タイミングの問題を回避できます。 - 適切なシグナルを選ぶ
目的によって適切なシグナルを選択します。- 単一アイテムのクリックに反応する場合:
itemClicked(QListWidgetItem*)
またはitemPressed(QListWidgetItem*)
を使用し、引数のQListWidgetItem*
を直接使用する。 - 選択状態の変更全体に反応する場合(複数選択を含む):
itemSelectionChanged()
シグナルを使用し、その中でselectedItems()
を呼び出す。 - キーボードフォーカスを持つアイテムの変更に反応する場合:
currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
を使用し、current
引数を使用する。
- 単一アイテムのクリックに反応する場合:
アイテムがヒープに確保されていない
エラーの原因
これは QListWidgetItem
の作成方法に起因するもので、currentItem()
自体の問題ではありませんが、結果的にアイテムが正しく動作しない原因となります。QListWidgetItem
をスタック(自動変数)で作成し、listWidget->addItem(&my_item)
のようにポインタを渡すと、スコープを抜けたときにアイテムが破棄され、ダングリングポインタになる可能性があります。
シナリオ
- 関数内で一時的に
QListWidgetItem
を作成し、それをリストに追加した後、関数が終了するとアイテムが消えてしまう。
トラブルシューティング
QListWidgetItem
は必ずヒープに(new
を使って)確保し、そのポインタを QListWidget
に渡すようにします。QListWidget
は追加されたアイテムの所有権を持つため、手動で delete
する必要はありません(QListWidget
が破棄される際に自動的に削除されます)。
// 悪い例: スタック変数
// QListWidgetItem myItem("Static Item");
// listWidget->addItem(&myItem); // myItemは関数終了時に破棄される
// 良い例: ヒープに確保
QListWidgetItem *newItem = new QListWidgetItem("Dynamic Item");
listWidget->addItem(newItem);
// newItemはlistWidgetによって所有され、listWidgetが破棄されるときに自動的に解放される
QListWidget::currentItem()
を使用する際のトラブルシューティングの鍵は、以下の点を理解することです。
nullptr
チェックを怠らない。currentItem()
とselectedItems()
の違いを理解する。 (単一のキーボードフォーカス vs. 複数の選択されたアイテム)- シグナルとスロットのタイミングを考慮し、適切なシグナルとその引数を使用する。
QListWidgetItem
はヒープに確保する。
QListWidget::currentItem()
は、QListWidget
で現在選択されている(ハイライトされている)アイテムを取得するために使われます。ここでは、いくつかの一般的なシナリオでの具体的なコード例を挙げ、それぞれについて解説します。
例1: 選択されたアイテムのテキストを表示する(基本)
最も基本的な使い方です。ユーザーがリストアイテムを選択したときに、そのテキストを別の場所(例: QLabel
やコンソール)に表示します。
#include <QApplication>
#include <QMainWindow>
#include <QListWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
#include <QDebug> // デバッグ出力用
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow window;
QWidget *centralWidget = new QWidget(&window);
window.setCentralWidget(centralWidget);
QVBoxLayout *layout = new QVBoxLayout(centralWidget);
QListWidget *listWidget = new QListWidget();
listWidget->addItem("Apple");
listWidget->addItem("Banana");
listWidget->addItem("Cherry");
listWidget->addItem("Date");
QLabel *selectedLabel = new QLabel("選択中のフルーツ: なし");
selectedLabel->setStyleSheet("font-size: 16px; font-weight: bold;");
// QListWidget の選択が変更されたときに slot が呼び出されるように接続
// currentItemChanged シグナルは、キーボードフォーカスを持つアイテムが変更されたときに発火します。
// 引数として新しい現在のアイテムと古い現在のアイテムが渡されます。
QObject::connect(listWidget, &QListWidget::currentItemChanged,
[=](QListWidgetItem *current, QListWidgetItem *previous) {
Q_UNUSED(previous); // previous は今回は使用しない
// current は新しい現在のアイテムへのポインタです。
// currentItem() と同じアイテムを指します。
// ここでは current を直接使用するのがより自然ですが、
// currentItem() の使用例としてあえて呼び出します。
QListWidgetItem *selectedItem = listWidget->currentItem(); // ここで currentItem() を使用
if (selectedItem) {
selectedLabel->setText("選択中のフルーツ: " + selectedItem->text());
qDebug() << "選択されたアイテム: " << selectedItem->text();
} else {
selectedLabel->setText("選択中のフルーツ: なし");
qDebug() << "何も選択されていません。";
}
});
layout->addWidget(listWidget);
layout->addWidget(selectedLabel);
window.setWindowTitle("currentItem() 例 1: テキスト表示");
window.resize(300, 200);
window.show();
return a.exec();
}
解説
- 取得したポインタが
nullptr
でないことを確認し(何も選択されていない可能性があるため)、そのアイテムのテキスト (selectedItem->text()
) をQLabel
に表示しています。 - このシグナルに接続されたラムダ関数内で、
listWidget->currentItem()
を呼び出して現在選択されているアイテムへのポインタを取得しています。 QListWidget::currentItemChanged
シグナルは、リストウィジェットの現在のアイテム(キーボードフォーカスを持つアイテム)が変更されたときに発火します。
例2: 選択されたアイテムを削除する
ユーザーが選択したアイテムをリストから削除する機能です。ここでも currentItem()
が活躍します。
#include <QApplication>
#include <QMainWindow>
#include <QListWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QDebug>
#include <QMessageBox> // 警告メッセージ用
int main(int argc, char *argv[])
{
QApplication a(argc);
QMainWindow window;
QWidget *centralWidget = new QWidget(&window);
window.setCentralWidget(centralWidget);
QVBoxLayout *layout = new QVBoxLayout(centralWidget);
QListWidget *listWidget = new QListWidget();
listWidget->addItem("Item A");
listWidget->addItem("Item B");
listWidget->addItem("Item C");
listWidget->addItem("Item D");
QPushButton *deleteButton = new QPushButton("選択中のアイテムを削除");
// 削除ボタンがクリックされたときに呼び出されるスロット
QObject::connect(deleteButton, &QPushButton::clicked,
[=]() {
QListWidgetItem *currentItem = listWidget->currentItem(); // ここで currentItem() を使用
if (currentItem) {
// 現在の行番号を取得
int row = listWidget->row(currentItem);
qDebug() << "削除されるアイテム: " << currentItem->text() << " (行: " << row << ")";
// アイテムをリストから削除
// takeItem はアイテムの所有権を呼び出し元に移すため、手動で delete する必要があります。
// delete item を呼び出すか、QListWidget::removeItemWidget() なども検討できます。
// 最も簡単なのは listWidget->removeItemWidget(currentItem) を使ってから delete currentItem することです。
// または、delete currentItem; // QListWidget がアイテムの所有権を持つ場合、通常は自動的に削除されるため、
// // takeItem() を使わない限り、明示的な delete は不要です。
// より一般的な削除方法:
delete listWidget->takeItem(row); // takeItem でアイテムをリストから外し、その後 delete する
QMessageBox::information(&window, "削除完了", "アイテム '" + currentItem->text() + "' を削除しました。");
} else {
QMessageBox::warning(&window, "警告", "削除するアイテムが選択されていません。");
qDebug() << "削除するアイテムが選択されていません。";
}
});
layout->addWidget(listWidget);
layout->addWidget(deleteButton);
window.setWindowTitle("currentItem() 例 2: アイテム削除");
window.resize(300, 250);
window.show();
return a.exec();
}
解説
delete listWidget->takeItem(row);
は、選択されたアイテムをリストウィジェットから削除する一般的な方法です。takeItem(row)
は指定された行のアイテムのポインタを返し、そのアイテムはリストウィジェットの所有から外れます。そのため、返されたポインタをdelete
することで、メモリを解放する必要があります。listWidget->row(currentItem)
で、選択されたアイテムの行番号を取得します。if (currentItem)
でnullptr
チェックを行い、アイテムが選択されている場合のみ処理を進めます。listWidget->currentItem()
で現在選択されているアイテムを取得します。- 「選択中のアイテムを削除」ボタンがクリックされたときに、ラムダ関数が実行されます。
例3: 選択されたアイテムのデータを操作する
QListWidgetItem
には、表示されるテキストだけでなく、関連するカスタムデータを保存することができます。currentItem()
を使ってアイテムを取得し、そのカスタムデータを操作する例です。
#include <QApplication>
#include <QMainWindow>
#include <QListWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QDebug>
#include <QVariant> // QVariant を使用するため
int main(int argc, char *argv[])
{
QApplication a(argc);
QMainWindow window;
QWidget *centralWidget = new QWidget(&window);
window.setCentralWidget(centralWidget);
QVBoxLayout *layout = new QVBoxLayout(centralWidget);
QListWidget *listWidget = new QListWidget();
// アイテムにカスタムデータを追加
QListWidgetItem *item1 = new QListWidgetItem("User: Alice");
item1->setData(Qt::UserRole, 101); // ユーザーIDをカスタムデータとして保存
listWidget->addItem(item1);
QListWidgetItem *item2 = new QListWidgetItem("User: Bob");
item2->setData(Qt::UserRole, 102);
listWidget->addItem(item2);
QListWidgetItem *item3 = new QListWidgetItem("User: Charlie");
item3->setData(Qt::UserRole, 103);
listWidget->addItem(item3);
QPushButton *showDataButton = new QPushButton("選択中のユーザーデータを表示");
QObject::connect(showDataButton, &QPushButton::clicked,
[=]() {
QListWidgetItem *currentItem = listWidget->currentItem(); // ここで currentItem() を使用
if (currentItem) {
QString userName = currentItem->text();
// setData() で保存したデータを data() で取得
// Qt::UserRole はカスタムデータのロールとしてよく使われます。
int userId = currentItem->data(Qt::UserRole).toInt();
qDebug() << "選択中のユーザー: " << userName;
qDebug() << "ユーザーID: " << userId;
QMessageBox::information(&window, "ユーザーデータ",
"ユーザー名: " + userName + "\nID: " + QString::number(userId));
} else {
QMessageBox::warning(&window, "警告", "データ表示するユーザーが選択されていません。");
qDebug() << "データ表示するユーザーが選択されていません。";
}
});
layout->addWidget(listWidget);
layout->addWidget(showDataButton);
window.setWindowTitle("currentItem() 例 3: データ操作");
window.resize(300, 250);
window.show();
return a.exec();
}
currentItem->data(Qt::UserRole).toInt()
を使って、保存しておいたユーザーIDをint
型で取り出しています。QVariant::toInt()
はQVariant
の内容をint
に変換します。- ボタンがクリックされたときに、
currentItem()
で選択されたアイテムを取得します。 QListWidgetItem
を作成する際に、setData(Qt::UserRole, value)
を使って、アイテムに関連する任意のデータをQVariant
型で保存しています。Qt::UserRole
は、Qt の内部で予約されているロール(Qt::DisplayRole
など)と衝突しないように、カスタムデータに使うための予約された値です。
QListWidget::selectedItems() (複数選択モードで必須)
これは currentItem()
の最も重要な代替であり、特に複数選択モードを使用している場合に不可欠です。
- 注意点
何も選択されていない場合、空のリストを返します。 - いつ使うか
QListWidget::setSelectionMode()
でQListWidget::ExtendedSelection
またはQListWidget::MultiSelection
を設定している場合。- ユーザーがCtrlキーやShiftキーを使って複数のアイテムを選択する可能性がある場合。
- 選択された複数のアイテムに対して一括で処理を行う必要がある場合(例: 一括削除、一括コピーなど)。
- 戻り値
QList<QListWidgetItem*>
(選択されたアイテムのポインタのリスト)。 - 目的
現在選択されているすべてのアイテムのリストを取得する。
使用例
// 複数選択が可能なリストウィジェットを想定
QList<QListWidgetItem*> selectedItems = listWidget->selectedItems();
if (!selectedItems.isEmpty()) {
qDebug() << "選択されているアイテム (" << selectedItems.count() << "個):";
for (QListWidgetItem *item : selectedItems) {
qDebug() << " - " << item->text();
// 選択された各アイテムに対する処理
}
} else {
qDebug() << "何も選択されていません。";
}
シグナルの引数を利用する (itemClicked, itemActivated, currentItemChanged など)
QListWidget
は様々なシグナルを発行しており、その引数として関連するアイテムのポインタを渡してくれることがあります。これにより、明示的に currentItem()
を呼び出す必要がなくなります。
-
currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
- 目的
キーボードフォーカスを持つ「現在のアイテム」が変更されたときに反応する。 - 引数
新しい現在のアイテム (current
) と古い現在のアイテム (previous
) のポインタ。 - いつ使うか
currentItem()
が返すアイテムに連動してUIを更新したい場合。- シングル選択モードで、ユーザーが別のアイテムを選択したときに即座に反応したい場合。
- 注意点
current
引数は、このシグナルが発火した時点でのcurrentItem()
の戻り値と全く同じアイテムを指します。したがって、このシグナル内でcurrentItem()
を呼び出すことは冗長であり、代わりにcurrent
引数を直接使用すべきです。
使用例
QObject::connect(listWidget, &QListWidget::currentItemChanged, [](QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous); // 古いアイテムは今回は使用しない if (current) { qDebug() << "新しい現在のアイテム: " << current->text(); } else { qDebug() << "現在のアイテムが設定されていません (選択解除など)。"; } });
- 目的
-
itemActivated(QListWidgetItem *item)
- 目的
ユーザーがアイテムを「アクティベート」したときに(通常はダブルクリックまたはEnterキーを押したとき)、アクティベートされた特定のアイテムに反応する。 - 引数
アクティベートされたQListWidgetItem
のポインタ。 - いつ使うか
アイテムをダブルクリックしたときに詳細表示を開くなど、主要なアクションをトリガーしたい場合。
使用例
QObject::connect(listWidget, &QListWidget::itemActivated, [](QListWidgetItem *item) { qDebug() << "アクティベートされたアイテム: " << item->text(); // アクティベートされたアイテムに対する処理(例: ダイアログを開く) });
- 目的
-
itemClicked(QListWidgetItem *item)
- 目的
ユーザーがアイテムをクリックしたときに、クリックされた特定のアイテムに反応する。 - 引数
クリックされたQListWidgetItem
のポインタ。 - いつ使うか
シングルクリックでアクションをトリガーしたい場合。 - 注意点
このシグナルは、クリックされた時点のアイテムを指します。複数選択モードでクリックすると、そのアイテムが現在のアイテムになることがありますが、選択状態全体が変更されたわけではない場合があります(Ctrlクリックなど)。
QObject::connect(listWidget, &QListWidget::itemClicked, [](QListWidgetItem *item) { qDebug() << "クリックされたアイテム: " << item->text(); // クリックされたアイテムに対する処理 });
- 目的
QListWidget::item(int row) (行番号が分かっている場合)
特定の行番号に対応するアイテムを取得したい場合に使用します。
- いつ使うか
- アイテムを反復処理する際。
- プログラム的に特定の行のアイテムにアクセスする必要がある場合。
QListWidget::currentRow()
と組み合わせて、現在の行のアイテムを取得する場合(これは実質的にcurrentItem()
と同じ結果になりますが、概念的な代替として)。
- 戻り値
指定された行のQListWidgetItem
のポインタ。 - 引数
取得したいアイテムの行番号(0から始まる)。 - 目的
特定の行にあるアイテムのポインタを取得する。
使用例
// 最初のアイテムのテキストを取得
if (listWidget->count() > 0) {
QListWidgetItem *firstItem = listWidget->item(0);
qDebug() << "最初のアイテム: " << firstItem->text();
}
// 現在選択されている行のアイテムを取得 (currentItem() とほぼ同じ)
int currentRow = listWidget->currentRow();
if (currentRow != -1) { // -1 は何も選択されていないことを意味する
QListWidgetItem *itemAtCurrentRow = listWidget->item(currentRow);
qDebug() << "現在の行のアイテム: " << itemAtCurrentRow->text();
}