Qt 初心者向け!QListWidget::findItems() でリスト操作をマスター

2025-05-31

基本的な使い方

QListWidget::findItems(const QString &text, Qt::MatchFlags flags) const

この関数は2つの引数を取ります。

  1. const QString &text: 検索したい文字列です。この文字列とアイテムのテキストを比較して、一致するアイテムを探します。
  2. Qt::MatchFlags flags: 検索の条件や方法を指定するためのフラグです。Qt::MatchFlags はビットマスクとして定義されており、複数のフラグを | (ビットOR演算子) で組み合わせることができます。

主な Qt::MatchFlags の種類

  • Qt::MatchRecursive: (通常 QListWidget では効果がありません。ツリー構造を持つ QTreeWidget などで使用されます。)
  • Qt::MatchWrap: リストの末尾まで検索した後、先頭に戻って検索を継続します。
  • Qt::MatchCaseSensitive: 大文字・小文字を区別して検索を行います。このフラグがない場合は、大文字・小文字を区別しません。
  • Qt::MatchEndsWith: アイテムのテキストが text で終わる場合にマッチします。
  • Qt::MatchStartsWith: アイテムのテキストが text で始まる場合にマッチします。
  • Qt::MatchContains: アイテムのテキストが text を含んでいる場合にマッチします。
  • Qt::MatchExactly: アイテムのテキストが完全に text と一致する場合のみマッチします。

戻り値

関数は、条件に一致するすべての QListWidgetItem オブジェクトへのポインタを含む QList<QListWidgetItem *> を返します。一致するアイテムが見つからなかった場合は、空のリストが返されます。

// my_list_widget は QListWidget のインスタンスとします
QListWidget *my_list_widget = new QListWidget();
my_list_widget->addItem("Apple");
my_list_widget->addItem("Banana");
my_list_widget->addItem("Orange");
my_list_widget->addItem("Green Apple");

// "Apple" と完全に一致するアイテムを検索
QList<QListWidgetItem *> exactMatches = my_list_widget->findItems("Apple", Qt::MatchExactly);
qDebug() << "完全一致:" << exactMatches.count() << "件"; // 出力: 完全一致: 1 件

// "Apple" を含むアイテムを検索 (大文字・小文字を区別しない)
QList<QListWidgetItem *> containsMatches = my_list_widget->findItems("apple", Qt::MatchContains);
qDebug() << "部分一致 (大文字・小文字無視):" << containsMatches.count() << "件"; // 出力: 部分一致 (大文字・小文字無視): 2 件

// "Apple" を含むアイテムを検索 (大文字・小文字を区別する)
QList<QListWidgetItem *> caseSensitiveMatches = my_list_widget->findItems("Apple", Qt::MatchContains | Qt::MatchCaseSensitive);
qDebug() << "部分一致 (大文字・小文字区別):" << caseSensitiveMatches.count() << "件"; // 出力: 部分一致 (大文字・小文字区別): 2 件

// "Orange" で始まるアイテムを検索
QList<QListWidgetItem *> startsWithMatches = my_list_widget->findItems("Ora", Qt::MatchStartsWith);
qDebug() << "前方一致:" << startsWithMatches.count() << "件"; // 出力: 前方一致: 1 件

// 検索結果のアイテムを処理する
for (QListWidgetItem *item : exactMatches) {
    qDebug() << "見つかったアイテムのテキスト:" << item->text(); // 出力: 見つかったアイテムのテキスト: Apple
}

delete my_list_widget;


検索結果が空である(期待したアイテムが見つからない)

  • トラブルシューティング

    • 検索文字列を再度確認
      スペルミス、大文字・小文字の違いがないか丁寧に確認してください。
    • マッチフラグを見直す
      目的の検索方法に合ったフラグを使用しているか確認してください。部分一致を期待するなら Qt::MatchContains、前方一致なら Qt::MatchStartsWith など。
    • リストの内容を確認
      検索前にリストに期待するアイテムが存在するか確認してください。
    • アイテムのテキストを確認
      item->text() を呼び出して、実際にアイテムに設定されているテキストが検索文字列と一致するか確認してください。
    • qDebug() を活用
      検索結果のリストのサイズ (count()) や、見つかったアイテムのテキストを qDebug() で出力して確認すると、原因特定に役立ちます。
    • 検索文字列のスペルミスや大文字・小文字の不一致
      Qt::MatchExactly を使用している場合や、Qt::MatchCaseSensitive フラグを設定している場合に起こりやすいです。
    • マッチフラグの誤り
      意図した検索方法と異なるフラグを使用している可能性があります。例えば、「含む」検索をしたいのに Qt::MatchExactly を使用しているなど。
    • アイテムがリストに追加されていない
      検索前に addItem() などでリストにアイテムが追加されているか確認してください。
    • 検索対象のテキストが期待するものと異なる
      アイテムに表示されているテキストと、実際に QListWidgetItem に設定されているテキストが異なる場合があります。item->text() で確認してみましょう。

期待しないアイテムまで検索結果に含まれてしまう

  • トラブルシューティング

    • より適切なマッチフラグを検討
      検索の意図に合わせて、より厳密なマッチフラグを使用できないか検討してください。
    • Qt::MatchCaseSensitive の使用
      大文字・小文字を区別する必要がある場合は、このフラグを追加してください。
  • 原因

    • マッチフラグが緩すぎる
      例えば、Qt::MatchContains を使用した場合、検索文字列がアイテムのテキストの一部に含まれていればマッチします。より厳密な検索が必要な場合は、Qt::MatchExactlyQt::MatchStartsWith などを検討してください。
    • 大文字・小文字の区別がない
      Qt::MatchCaseSensitive フラグを指定していない場合、大文字・小文字が区別されません。区別したい場合はこのフラグを追加してください。

検索パフォーマンスの問題(リストが大きい場合)

  • トラブルシューティング

    • 検索頻度を下げる
      可能であれば、検索の実行回数を減らすようにアプリケーションの設計を見直してください。
    • インデックスの利用(高度なテクニック)
      より複雑な検索要件がある場合は、リストの内容を効率的に検索できるような独自のデータ構造(例えば、テキストとアイテムの対応を保持する QMap など)を別途作成し、インデックスとして利用することを検討するのも一つの方法です。ただし、これは QListWidget::findItems() の直接的なトラブルシューティングではありません。
    • 部分的な検索
      ユーザーの入力に応じて逐次検索を行う場合、入力が確定してから検索を実行するなど、不要な検索を避ける工夫を検討してください。
  • 原因

    • リストが非常に大きい
      大量のアイテムを含むリストに対して頻繁に検索を行うと、パフォーマンスに影響が出る可能性があります。findItems() はリスト全体を走査するため、アイテム数が増えるほど処理時間も増加します。

const 関数であることの理解不足

  • 注意点
    findItems()const 関数です。つまり、この関数を呼び出しても QListWidget 自体の状態は変更されません。検索結果として返されるのは、条件に一致した QListWidgetItem オブジェクトへのポインタのリストです。これらのポインタを使用してアイテムの内容を変更することは可能ですが、QListWidget の構造自体を変更することはできません。

トラブルシューティングの一般的なヒント

  • Qt のドキュメントを参照
    QListWidget::findItems()Qt::MatchFlags の詳細な説明は、Qt の公式ドキュメントで確認できます。
  • qDebug() の活用
    検索文字列、使用しているフラグ、検索結果の数、見つかったアイテムのテキストなどを qDebug() で出力して、何が起こっているのかを詳しく把握しましょう。
  • 問題を切り分ける
    複雑な処理の中で findItems() が期待通りに動作しない場合は、まずは簡単なテストコードを作成し、findItems() 単独での動作を確認してみましょう。


例1: 完全一致でアイテムを検索し、選択する

この例では、ユーザーが入力した文字列と完全に一致するアイテムを QListWidget 内から検索し、見つかった最初のアイテムを選択状態にします。

#include <QApplication>
#include <QListWidget>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>

class MainWindow : public QWidget {
public:
    MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
        listWidget = new QListWidget();
        listWidget->addItem("りんご");
        listWidget->addItem("みかん");
        listWidget->addItem("バナナ");
        listWidget->addItem("青りんご");

        lineEdit = new QLineEdit();
        searchButton = new QPushButton("検索");

        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(listWidget);
        layout->addWidget(lineEdit);
        layout->addWidget(searchButton);

        connect(searchButton, &QPushButton::clicked, this, &MainWindow::searchAndSelectItem);
    }

private slots:
    void searchAndSelectItem() {
        QString searchText = lineEdit->text();
        QList<QListWidgetItem *> foundItems = listWidget->findItems(searchText, Qt::MatchExactly);

        if (!foundItems.isEmpty()) {
            // 最初に見つかったアイテムを選択
            listWidget->setCurrentItem(foundItems.first());
            qDebug() << "\"" << searchText << "\" に一致するアイテムが見つかりました。";
        } else {
            qDebug() << "\"" << searchText << "\" に一致するアイテムは見つかりませんでした。";
        }
    }

private:
    QListWidget *listWidget;
    QLineEdit *lineEdit;
    QPushButton *searchButton;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

解説

  • qDebug() で検索結果をコンソールに出力します。
  • 検索結果が空でなければ、最初に見つかったアイテムを setCurrentItem() で選択状態にします。
  • findItems(searchText, Qt::MatchExactly) で、入力された文字列と完全に一致するアイテムを検索します。
  • 「検索」ボタンがクリックされると、searchAndSelectItem() スロットが呼び出されます。
  • QLineEdit でユーザーが検索したい文字列を入力できるようにしています。
  • QListWidget にいくつかの果物の名前を追加しています。

例2: 部分一致でアイテムを検索し、背景色を変更する

この例では、ユーザーが入力した文字列を部分的に含むアイテムを検索し、見つかったアイテムの背景色を黄色に変更します。

#include <QApplication>
#include <QListWidget>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QPushButton>
#include <QColor>
#include <QBrush>
#include <QDebug>

class MainWindow : public QWidget {
public:
    MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
        listWidget = new QListWidget();
        listWidget->addItem("Qt 6 プログラミング");
        listWidget->addItem("C++ の基礎");
        listWidget->addItem("Qt Designer の使い方");
        listWidget->addItem("実践 Qt アプリケーション開発");

        lineEdit = new QLineEdit();
        searchButton = new QPushButton("部分一致検索");

        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(listWidget);
        layout->addWidget(lineEdit);
        layout->addWidget(searchButton);

        connect(searchButton, &QPushButton::clicked, this, &MainWindow::searchAndHighlightItems);
    }

private slots:
    void searchAndHighlightItems() {
        QString searchText = lineEdit->text();
        QList<QListWidgetItem *> foundItems = listWidget->findItems(searchText, Qt::MatchContains);

        // まず、すべてのアイテムの背景色を元に戻す (必要に応じて)
        for (int i = 0; i < listWidget->count(); ++i) {
            listWidget->item(i)->setBackground(QBrush(Qt::white));
        }

        if (!foundItems.isEmpty()) {
            for (QListWidgetItem *item : foundItems) {
                item->setBackground(QBrush(Qt::yellow));
            }
            qDebug() << "\"" << searchText << "\" を含むアイテムが見つかりました:" << foundItems.count() << "件";
        } else {
            qDebug() << "\"" << searchText << "\" を含むアイテムは見つかりませんでした。";
        }
    }

private:
    QListWidget *listWidget;
    QLineEdit *lineEdit;
    QPushButton *searchButton;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

解説

  • qDebug() で検索結果の件数をコンソールに出力します。
  • 見つかった各アイテムの背景色を黄色に変更します。
  • 検索前に、念のためすべてのアイテムの背景色を白に戻しています。
  • findItems(searchText, Qt::MatchContains) で、入力された文字列を部分的に含むアイテムを検索します。
  • 「部分一致検索」ボタンがクリックされると、searchAndHighlightItems() スロットが呼び出されます。
  • QListWidget にプログラミング関連の文字列を追加しています。

例3: 大文字・小文字を区別して検索する

この例では、ユーザーが入力した文字列と部分的に一致するアイテムを、大文字・小文字を区別して検索します。

#include <QApplication>
#include <QListWidget>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>

class MainWindow : public QWidget {
public:
    MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
        listWidget = new QListWidget();
        listWidget->addItem("apple");
        listWidget->addItem("Apple Pie");
        listWidget->addItem("banana");
        listWidget->addItem("Orange");

        lineEdit = new QLineEdit();
        searchButton = new QPushButton("大文字・小文字区別検索");

        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(listWidget);
        layout->addWidget(lineEdit);
        layout->addWidget(searchButton);

        connect(searchButton, &QPushButton::clicked, this, &MainWindow::searchCaseSensitive);
    }

private slots:
    void searchCaseSensitive() {
        QString searchText = lineEdit->text();
        Qt::MatchFlags flags = Qt::MatchContains | Qt::MatchCaseSensitive;
        QList<QListWidgetItem *> foundItems = listWidget->findItems(searchText, flags);

        qDebug() << "\"" << searchText << "\" (大文字・小文字区別あり) を含むアイテム:";
        if (!foundItems.isEmpty()) {
            for (QListWidgetItem *item : foundItems) {
                qDebug() << "- " << item->text();
            }
        } else {
            qDebug() << "見つかりませんでした。";
        }
    }

private:
    QListWidget *listWidget;
    QLineEdit *lineEdit;
    QPushButton *searchButton;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
  • 検索結果を qDebug() で出力し、見つかったアイテムのテキストを表示します。
  • findItems() の第二引数に Qt::MatchContains | Qt::MatchCaseSensitive を指定することで、部分一致かつ大文字・小文字を区別する検索を行います。


ループ処理による手動検索

最も基本的な方法は、QListWidget のすべてのアイテムに対してループ処理を行い、それぞれのアイテムのテキストを目的の条件で比較する方法です。

QList<QListWidgetItem *> manualFindItems(QListWidget *listWidget, const QString &searchText, Qt::MatchFlags flags) {
    QList<QListWidgetItem *> results;
    for (int i = 0; i < listWidget->count(); ++i) {
        QListWidgetItem *item = listWidget->item(i);
        QString itemText = item->text();

        bool matched = false;
        if (flags & Qt::MatchExactly) {
            matched = (itemText == searchText);
        } else if (flags & Qt::MatchContains) {
            matched = (itemText.contains(searchText));
        } else if (flags & Qt::MatchStartsWith) {
            matched = (itemText.startsWith(searchText));
        } else if (flags & Qt::MatchEndsWith) {
            matched = (itemText.endsWith(searchText));
        }

        if (flags & Qt::MatchCaseSensitive) {
            // すでに比較済み
        } else {
            matched = QString::compare(itemText, searchText, Qt::CaseInsensitive) == 0;
            if (flags & Qt::MatchContains) {
                matched = itemText.toLower().contains(searchText.toLower());
            } else if (flags & Qt::MatchStartsWith) {
                matched = itemText.toLower().startsWith(searchText.toLower());
            } else if (flags & Qt::MatchEndsWith) {
                matched = itemText.toLower().endsWith(searchText.toLower());
            }
        }

        if (matched) {
            results.append(item);
        }
    }
    return results;
}

// 使用例
QList<QListWidgetItem *> foundItems = manualFindItems(myListWidget, "apple", Qt::MatchContains | Qt::MatchCaseSensitive);

利点

  • 複数のプロパティ(テキスト以外)に基づいて検索できる。(例:アイテムのデータ、アイコンなど)
  • Qt::MatchFlags にない独自の検索ロジックを実装できる。

欠点

  • 最適化されていないため、大きなリストでは findItems() よりパフォーマンスが劣る可能性がある。
  • findItems() よりもコード量が多くなる。

データのモデルを利用する(QAbstractItemModel 派生クラスの場合)

QListWidget は内部的に QListWidgetItemModel を使用しています。より複雑なデータ管理やフィルタリングが必要な場合は、直接モデルを操作したり、QSortFilterProxyModel を使用したりすることが考えられます。

// QSortFilterProxyModel を使用してフィルタリングする例
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(myListWidget);
proxyModel->setSourceModel(myListWidget->model());
proxyModel->setFilterKeyColumn(0); // 0番目の列(テキスト)でフィルタリング
proxyModel->setFilterFixedString("apple"); // "apple" を含むアイテムでフィルタリング

QListView *filteredView = new QListView();
filteredView->setModel(proxyModel);

// filteredView には "apple" を含むアイテムのみが表示される

利点

  • QSortFilterProxyModel はソート機能も提供する。
  • データの表示とロジックを分離できるため、より構造化されたコードになる。
  • モデルのフィルタリング機能を利用できるため、複雑な条件での絞り込みが容易。

欠点

  • 単純な検索処理にはオーバースペックになる可能性がある。
  • QListWidget の直接的な操作よりも抽象度が高いため、理解と実装に手間がかかる場合がある。

アイテムのデータを利用した検索

QListWidgetItemsetData() 関数を使って任意のデータを関連付けることができます。この関連付けられたデータを利用して検索を行う方法です。

// アイテムにデータを設定
QListWidgetItem *item1 = new QListWidgetItem("りんご");
item1->setData(Qt::UserRole, "fruit");
myListWidget->addItem(item1);

QListWidgetItem *item2 = new QListWidgetItem("にんじん");
item2->setData(Qt::UserRole, "vegetable");
myListWidget->addItem(item2);

// データを元に検索
QList<QListWidgetItem *> findItemsByData(QListWidget *listWidget, const QVariant &data, int role) {
    QList<QListWidgetItem *> results;
    for (int i = 0; i < listWidget->count(); ++i) {
        QListWidgetItem *item = listWidget->item(i);
        if (item->data(role) == data) {
            results.append(item);
        }
    }
    return results;
}

// 使用例
QList<QListWidgetItem *> fruitItems = findItemsByData(myListWidget, "fruit", Qt::UserRole);
for (QListWidgetItem *item : fruitItems) {
    qDebug() << "果物:" << item->text(); // 出力: 果物: りんご
}

利点

  • より意味的な検索が可能になる。
  • テキスト以外の情報に基づいてアイテムを検索できる。

欠点

  • テキスト検索のような柔軟な文字列マッチングは手動で実装する必要がある。
  • 検索前にアイテムに適切なデータを設定しておく必要がある。

外部のデータ構造との連携

リストの内容が頻繁に検索される場合や、複雑な検索ロジックが必要な場合は、QListWidget の内容を外部のデータ構造(例:QMap, QSet など)に保持し、そのデータ構造に対して検索を行うことも考えられます。検索結果に基づいて QListWidget のアイテムを操作します。

利点

  • より複雑なデータ管理やインデックス作成が行える。
  • 外部のデータ構造の特性を活かした効率的な検索が可能になる場合がある。
  • 実装が複雑になる場合がある。
  • QListWidget の内容と外部データ構造の同期を保つ必要がある。