初心者必見!Qt QListWidgetでリスト表示を始めるプログラミング例

2025-05-31

QListWidget::QListWidget()は、QtプログラミングにおけるQListWidgetクラスのコンストラクタです。

これは、新しいQListWidgetオブジェクトを作成する際に呼び出される特別な関数です。

具体的な説明は以下の通りです。

  • QListWidget(): これがコンストラクタの名称です。クラス名と同じ名前を持つのがコンストラクタの特徴です。括弧()は、引数を受け取らないことを意味します。つまり、このコンストラクタは、QListWidgetのインスタンスを生成する際に、特別な初期設定情報を受け取らない、最も基本的なコンストラクタです。


  • : これはC++におけるスコープ解決演算子です。QListWidgetクラスのメンバーであることを示します。

  • QListWidget: これは、Qtフレームワークが提供するウィジェットの一つで、リスト形式で項目を表示・管理するためのものです。ファイルの一覧表示や、選択可能なオプションのリストなど、様々な用途で使われます。

どのようなときに使うのか?

新しいQListWidgetを作成して、GUIアプリケーションにリスト表示機能を追加したいときに使います。例えば、以下のようなコードでインスタンスを生成します。

// ヘッダーファイルでQListWidgetをインクルード
#include <QListWidget>

// ... どこかの関数やクラスのメンバーとして ...

QListWidget* myListWidget = new QListWidget(); // 新しいQListWidgetオブジェクトを作成


QListWidgetItemのメモリ管理(最も重要!)

  • トラブルシューティング: QListWidgetItemヒープに作成し、QListWidgetに所有権を渡す必要があります。QListWidgetは、追加されたQListWidgetItemの所有権を取得し、自身が破棄される際にそれらを自動的に解放します。

    // 良い例: QListWidgetItemをヒープに作成
    void MyWindow::addItemsToList() {
        QListWidgetItem* item = new QListWidgetItem("My Item"); // ヒープに作成
        ui->listWidget->addItem(item); // ポインタを渡す (QListWidgetが所有権を持つ)
    }
    

    あるいは、コンストラクタで直接QListWidgetを親として指定する方法もあります。

    // 良い例: QListWidgetを親として指定
    void MyWindow::addItemsToList() {
        // QListWidgetが親として指定されている場合、addItem()は不要
        // この方法で作成されたQListWidgetItemは、親のQListWidgetが破棄されるときに自動的に破棄される
        new QListWidgetItem("My Item", ui->listWidget); 
    }
    
  • エラー: QListWidgetItemをスタックに作成し、そのポインタをQListWidgetに渡してしまう。

    // 悪い例: QListWidgetItemをスタックに作成
    void MyWindow::addItemsToList() {
        QListWidgetItem item("My Item"); // スタックに作成
        ui->listWidget->addItem(&item);  // ポインタを渡す
        // itemは関数終了時に破棄されるため、listWidgetは無効なポインタを参照することになる
    }
    

    この場合、addItemsToList関数が終了するとitemオブジェクトはスコープ外となり破棄されます。しかし、QListWidgetは破棄されたオブジェクトへのポインタを持ち続けるため、後でアクセスしようとするとクラッシュや未定義動作が発生します。

QListWidgetが表示されない/更新されない

  • トラブルシューティング:
    • QListWidgetを適切なレイアウト(例: QVBoxLayout, QHBoxLayout, QGridLayout)に追加し、そのレイアウトをメインウィンドウなどのウィジェットに設定しているか確認してください。
    • QListWidgetのコンストラクタで親ウィジェットを指定していない場合、setParent()メソッドを使用して親を設定しているか確認してください。
    • show()メソッドが呼び出されているか確認してください。
    • 項目を追加した後に、UIがすぐに更新されない場合があります。特に大量の項目を追加する場合など。QApplication::processEvents()を呼び出すことで強制的にイベント処理を促すことができますが、パフォーマンスの問題を引き起こす可能性があるため、乱用は避けるべきです。通常は、項目追加後にQListWidgetが自動的に再描画されます。
  • エラー: QListWidgetがレイアウトに追加されていない、または親ウィジェットに設定されていないため、画面に表示されない。

大量の項目追加時のパフォーマンス問題

  • トラブルシューティング:
    • setUniformItemSizes(true)を使用する: リスト内のすべての項目が同じサイズである場合、ui->listWidget->setUniformItemSizes(true);を呼び出すことで描画パフォーマンスが大幅に向上します。Qtは各項目のサイズを個別に計算する必要がなくなるためです。
    • updatesEnabled(false) / updatesEnabled(true)で更新を一時停止する: 大量の項目を追加する前にui->listWidget->setUpdatesEnabled(false);を呼び出し、追加後にui->listWidget->setUpdatesEnabled(true);を呼び出すことで、中間描画をスキップし、最後に一度だけ描画を更新させることができます。
    • モデル/ビューアーキテクチャの検討: 非常に大量のデータ(数千、数万項目以上)を扱う場合は、QListWidgetではなく、より低レベルなモデル/ビューのフレームワーク(QListViewとカスタムモデル QAbstractListModelの派生クラス)を使用することを検討してください。これにより、メモリ使用量と描画効率が大幅に改善されます。QListWidgetはあくまで利便性のためのラッパークラスであり、大規模なデータには向いていません。
  • エラー: 大量のQListWidgetItemを繰り返しaddItem()insertItem()で追加すると、アプリケーションの動作が非常に遅くなる。

シグナル/スロット接続の問題

  • トラブルシューティング:
    • connect()関数の引数が正しいか確認してください(シグナルの完全なシグネチャ、スロットの完全なシグネチャ)。
    • スロット関数がslots:セクションで宣言されているか、またはC++11ラムダ式を使用しているか確認してください。
    • オブジェクトの寿命を確認してください。シグナルを発信するオブジェクトとスロットを持つオブジェクトの両方が、接続中に有効な状態である必要があります。
  • エラー: QListWidgetのシグナル(例: itemClicked(QListWidgetItem*), currentItemChanged(QListWidgetItem*, QListWidgetItem*))を接続しても、スロットが呼び出されない。

QListWidgetItemの内容が正しく表示されない

  • トラブルシューティング:
    • QListWidgetItem::setText()QListWidgetItem::setIcon()が正しく呼び出されているか確認してください。
    • カスタムウィジェットを項目に設定している場合(setItemWidget()を使用する場合)、そのカスタムウィジェットが正しく描画されているか、また適切なサイズヒント(sizeHint())を提供しているか確認してください。
  • エラー: テキストやアイコンを設定しても、期待通りに表示されない。


QListWidget::QListWidget() コンストラクタ自体は非常に単純で、インスタンスを作成するだけなので、このコンストラクタに特化した「プログラミング例」というよりは、QListWidget を初期化し、基本的な操作を行う例として説明します。

QListWidget::QListWidget() は、新しい QListWidget オブジェクトをメモリ上に生成する役割しか持たないため、その後の使い方が重要になります。

例1: 基本的なQListWidgetの作成と項目の追加

この例では、QListWidget を作成し、いくつかのテキスト項目を追加する方法を示します。

// mainwindow.h (または任意のウィジェットのヘッダーファイル)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QListWidget> // QListWidgetを使用するためにインクルード
#include <QVBoxLayout> // レイアウトのためにインクルード
#include <QWidget>     // 中央ウィジェットのためにインクルード

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QListWidget *myListWidget; // QListWidgetのポインタをメンバーとして宣言
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // QListWidget::QListWidget() を呼び出してインスタンスを生成
    myListWidget = new QListWidget(this); // `this` を親として指定 (オプションだが推奨)

    // 項目を追加
    // 注意: QListWidgetItem はヒープに作成する必要があります
    myListWidget->addItem(new QListWidgetItem("Apple"));
    myListWidget->addItem(new QListWidgetItem("Banana"));
    myListWidget->addItem(new QListWidgetItem("Cherry"));

    // アイコン付きの項目を追加
    QListWidgetItem* itemWithIcon = new QListWidgetItem(QIcon(":/icons/star.png"), "Date"); // 仮のアイコンパス
    myListWidget->addItem(itemWithIcon);

    // QListWidget をレイアウトに追加し、ウィンドウに設定
    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(myListWidget);

    QWidget *centralWidget = new QWidget(this);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);

    setWindowTitle("QListWidget Basic Example");
}

MainWindow::~MainWindow()
{
    // QListWidget は親が破棄されるときに自動的に破棄されるため、
    // ここで delete myListWidget; を明示的に呼び出す必要はありません。
    // しかし、親を持たない場合は手動で解放する必要があります。
}

解説

  • QIcon を使用して、アイコン付きの項目も追加できます。":/icons/star.png" はQtのリソースシステムを使用したパスの例です。
  • addItem(new QListWidgetItem("...")); で、新しい QListWidgetItem をヒープに作成し、そのポインタを QListWidget に渡しています。QListWidget は、追加された QListWidgetItem の所有権を取得し、適切に管理します。
  • myListWidget = new QListWidget(this); の部分が QListWidget::QListWidget() コンストラクタを呼び出しています。引数に this を渡すことで、MainWindowmyListWidget の親として設定しています。これにより、MainWindow が破棄される際に myListWidget も自動的に破棄され、メモリリークを防ぐことができます。

例2: 項目の選択とシグナル/スロット

この例では、QListWidget の項目がクリックされたときに、どの項目が選択されたかを表示する方法を示します。

// mainwindow.h (上記例から一部変更)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QListWidget>
#include <QVBoxLayout>
#include <QWidget>
#include <QDebug>      // デバッグ出力のためにインクルード

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots: // スロットを宣言
    void onItemClicked(QListWidgetItem *item); // 項目がクリックされたときに呼ばれるスロット

private:
    Ui::MainWindow *ui;
    QListWidget *myListWidget;
};
#endif // MAINWINDOW_H
// mainwindow.cpp (上記例から一部変更)
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    myListWidget = new QListWidget(this);

    myListWidget->addItem(new QListWidgetItem("Apple"));
    myListWidget->addItem(new QListWidgetItem("Banana"));
    myListWidget->addItem(new QListWidgetItem("Cherry"));

    // シグナルとスロットの接続
    // myListWidget の itemClicked(QListWidgetItem*) シグナルが発せられたら、
    // このMainWindowクラスの onItemClicked(QListWidgetItem*) スロットを呼び出す
    connect(myListWidget, &QListWidget::itemClicked,
            this, &MainWindow::onItemClicked);

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(myListWidget);

    QWidget *centralWidget = new QWidget(this);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);

    setWindowTitle("QListWidget Signal/Slot Example");
}

MainWindow::~MainWindow()
{
}

// スロットの実装
void MainWindow::onItemClicked(QListWidgetItem *item)
{
    // クリックされた項目のテキストを表示
    qDebug() << "Clicked item:" << item->text();
}

解説

  • スロット関数 onItemClicked では、受け取った QListWidgetItemtext() メソッドを呼び出すことで、項目のテキストを取得し、デバッグ出力しています。
  • itemClicked(QListWidgetItem *item) シグナルは、クリックされた QListWidgetItem へのポインタを引数として渡します。
  • connect() 関数を使用して、myListWidgetitemClicked シグナルと、MainWindow クラスの onItemClicked スロットを接続しています。

QListWidget の内容をすべて削除する方法です。

// MainWindowクラス内に以下の関数を追加するイメージ
void MainWindow::clearList() {
    myListWidget->clear(); // すべての項目を削除し、QListWidgetItemのメモリも解放される
}
  • clear() メソッドを呼び出すだけで、QListWidget に追加されたすべての QListWidgetItem が削除され、関連するメモリも自動的に解放されます。非常に便利で安全な方法です。


しかし、「QListWidget を使ってリストを表示・操作する」という文脈で代替方法を考える場合、Qtにはより柔軟で強力なモデル/ビューアーキテクチャという選択肢があります。

QListWidget は、Qtのモデル/ビューアーキテクチャの上に構築されたコンビニエンス(簡易)クラスです。つまり、内部的にはQListViewQStringListModel(またはそれに似たもの)を組み合わせて機能を提供しています。

QListWidget は手軽で簡単なリスト表示には非常に便利ですが、以下のような場合に限界があります。

  • ソートやフィルタリングの高度な要件
    データのソートやフィルタリングを柔軟にカスタマイズしたい場合。
  • データの共有
    同じデータを複数のビュー(例えば、リストとテーブルの両方で同じデータを表示したい場合)で共有したい場合。
  • カスタム表示の複雑さ
    各項目の表示を非常に細かく制御したい場合(複雑なレイアウト、複数のウィジェットの組み合わせなど)、QListWidgetsetItemWidget() では限界があります。
  • 大量のデータ
    数千、数万といった大量の項目を扱う場合、QListWidget はメモリ使用量やパフォーマンスの面で非効率になることがあります。すべての項目データがメモリ上に一度にロードされるためです。

これらの高度な要件を満たすために、Qtはモデル/ビューアーキテクチャを提供しています。このアーキテクチャは以下の3つの主要な概念に分かれます。

  1. モデル (Model)
    データを保持し、ビューやデリゲートにデータを提供します。QAbstractListModelQAbstractTableModelQAbstractItemModel などの抽象クラスを継承して、カスタムモデルを作成します。
  2. ビュー (View)
    モデルからデータを受け取り、ユーザーに表示します。QListView(リスト形式)、QTableView(テーブル形式)、QTreeView(ツリー形式)などがあります。
  3. デリゲート (Delegate)
    ビュー内の各項目の描画方法や、編集時のウィジェット(エディタ)を提供します。QStyledItemDelegate を継承してカスタムデリゲートを作成します。

QListWidget の代替としての QListView とカスタムモデルの例

最も直接的な QListWidget の代替は、QListViewQStringListModel を組み合わせる方法です。

メリット

  • 大規模データセットに適している。
  • より柔軟なカスタマイズが可能(デリゲートを使って描画を詳細に制御できる)。
  • QListWidgetよりもメモリ効率が良い場合がある。

コード例(QListView と QStringListModel)

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QListView>        // QListViewを使用するためにインクルード
#include <QStringListModel> // QStringListModelを使用するためにインクルード
#include <QVBoxLayout>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QListView *myListView;           // QListViewのポインタ
    QStringListModel *myListModel;   // モデルのポインタ
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    myListView = new QListView(this);
    myListModel = new QStringListModel(this); // モデルをインスタンス化

    // モデルにデータを設定
    QStringList data;
    data << "Apple" << "Banana" << "Cherry" << "Date";
    myListModel->setStringList(data);

    // ビューにモデルを設定
    myListView->setModel(myListModel);

    // QListWidgetと同じようにシグナル/スロットを接続することも可能
    // myListView->setSelectionMode(QAbstractItemView::SingleSelection);
    // connect(myListView->selectionModel(), &QItemSelectionModel::currentChanged,
    //         this, [&](const QModelIndex &current, const QModelIndex &previous) {
    //     qDebug() << "Selected item (row):" << current.row() << " Text:" << current.data().toString();
    // });

    // レイアウトにビューを追加
    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(myListView);

    QWidget *centralWidget = new QWidget(this);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);

    setWindowTitle("QListView with QStringListModel Example");
}

MainWindow::~MainWindow()
{
    // モデルとビューは親が破棄されるときに自動的に破棄される
}

解説

  • 項目がクリックされたときの処理は、QListWidgetitemClicked とは異なり、QListViewselectionModel() から提供されるシグナル(例: currentChanged)を使用します。これにより、選択された項目のQModelIndex(モデル内の位置を表す)を取得し、そこからデータにアクセスします。
  • myListView->setModel(myListModel); の行で、ビューとモデルを関連付けています。これにより、ビューはモデルからデータを取得し、表示します。
  • QStringListModel は、QStringList を基にしてリストデータを提供する非常にシンプルなモデルです。
  • QListView を作成し、そのビューに表示するデータを管理する QStringListModel を作成しています。

QListWidget::QListWidget() コンストラクタ自体には代替がありませんが、QListWidget の機能全体を代替するより高度な方法として、Qtのモデル/ビューアーキテクチャがあります。

  • 複雑なデータ構造、大量のデータ、高度なカスタマイズが必要な場合
    QListView (または QTableView, QTreeView) と、QAbstractListModel (または他のQAbstractItemModel派生クラス) を継承したカスタムモデルを使用するのが、より強力で効率的なアプローチです。
  • 簡単なリスト表示、項目数が少ない場合
    QListWidget が最も手軽で推奨されます。