このページでは

SQL Widget Mapper の例

「SQLウィジェットマッパー」のサンプルでは、データベースの情報をフォーム上のウィジェットにマッピングして使用する方法を示しています。

メインウィンドウには、ユーザー名とユーザーの住所が表示され、住所の種類が分類されます。

「Combo Widget Mapper」の例では、ウィジェットマッパーと、特別な目的のモデルを持つQComboBox ウィジェットとの間で、名前付きマッピングを使用して、モデル内の値を選択肢のリストに関連付ける方法を紹介しました。

ここでも、ほぼ同じユーザーインターフェースを持つWindow クラスを作成し、住所を「自宅」、「職場」、「その他」に分類できるコンボボックスを提供します。ただし、これらの住所タイプを格納するために別のモデルを使用する代わりに、1つのデータベーステーブルにサンプルデータを格納し、別のテーブルに住所タイプを格納します。 このようにして、すべての情報を同じ場所に格納します。

ウィンドウクラスの定義

このクラスは、コンストラクタ、ボタンの状態を最新に保つためのスロット、およびモデルを設定するためのプライベート関数を提供します:

class Window : public QWidget
{
    Q_OBJECT

public:
    Window(QWidget *parent = nullptr);

private slots:
    void updateButtons(int row);

private:
    void setupModel();

    QLabel *nameLabel;
    QLabel *addressLabel;
    QLabel *typeLabel;
    QLineEdit *nameEdit;
    QTextEdit *addressEdit;
    QComboBox *typeComboBox;
    QPushButton *nextButton;
    QPushButton *previousButton;

    QSqlRelationalTableModel *model;
    QItemSelectionModel *selectionModel;
    QDataWidgetMapper *mapper;
    int typeIndex;
};

QDataWidgetMapper オブジェクトやユーザーインターフェースを構成するコントロールに加え、データを保持するためにQStandardItemModelを、各人物のデータに適用可能な住所の種類に関する情報を保持するためにQStringListModel を使用します。

ウィンドウクラスの実装

Window クラスのコンストラクタが最初に行う処理は、サンプルデータを保持するために使用するモデルの設定です。これは本例の重要な部分であるため、まずこれから見ていきます。

モデルは、ウィンドウのsetupModel() 関数内で初期化されます。ここでは、主キー、名前、住所、およびタイプというフィールドを持つ「person」テーブルを含むSQLiteデータベースを作成します。

void Window::setupModel()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if (!db.open()) {
        QMessageBox::critical(0, tr("Cannot open database"),
            tr("Unable to establish a database connection.\n"
               "This example needs SQLite support. Please read "
               "the Qt SQL driver documentation for information how "
               "to build it."), QMessageBox::Cancel);
        return;
    }

    QSqlQuery query;
    query.exec("create table person (id int primary key, "
               "name varchar(20), address varchar(200), typeid int)");
    query.exec("insert into person values(1, 'Alice', "
               "'<qt>123 Main Street<br/>Market Town</qt>', 101)");
    query.exec("insert into person values(2, 'Bob', "
               "'<qt>PO Box 32<br/>Mail Handling Service"
               "<br/>Service City</qt>', 102)");
    query.exec("insert into person values(3, 'Carol', "
               "'<qt>The Lighthouse<br/>Remote Island</qt>', 103)");
    query.exec("insert into person values(4, 'Donald', "
               "'<qt>47338 Park Avenue<br/>Big City</qt>', 101)");
    query.exec("insert into person values(5, 'Emma', "
               "'<qt>Research Station<br/>Base Camp<br/>"
               "Big Mountain</qt>', 103)");

テーブルの各行に対して、これらのフィールドのデフォルト値を挿入します。これには、別のテーブルに格納されている住所タイプに対応する値も含まれます。

メインウィンドウには、データベースに格納されている情報から成る表が表示されます。

「person」テーブルで使用される識別子と、それに対応する文字列を含む「addresstype」テーブルを作成します:

    query.exec("create table addresstype (id int, description varchar(20))");
    query.exec("insert into addresstype values(101, 'Home')");
    query.exec("insert into addresstype values(102, 'Work')");
    query.exec("insert into addresstype values(103, 'Other')");

    model = new QSqlRelationalTableModel(this);
    model->setTable("person");
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);

    typeIndex = model->fieldIndex("typeid");

    model->setRelation(typeIndex,
           QSqlRelation("addresstype", "id", "description"));
    model->select();
}

「person」テーブルの「typeid」フィールドは、QSqlRelationalTableModel 内のリレーションを介して「addresstype」テーブルの内容と関連付けられています。この種のモデルは、データベースへのデータ格納に必要なすべての処理を実行するだけでなく、あらゆるリレーションをそれ自体としてモデルとして利用できるようにします。

この例では、「person」テーブルの「typeid」フィールドと「addresstype」テーブルの「id」フィールドを関連付けるリレーションを定義しました。これにより、「typeid」がユーザーに表示される際には、常に「description」フィールドの内容が使用されるようになります。 (詳細については、QSqlRelationalTableModel::setRelation() のドキュメントを参照してください。)

この表は、「typeid」フィールドと「addresstype」の関係を示しています。

Window クラスのコンストラクタは、3つの部分に分けて説明できます。最初の部分では、データを保持するために使用されるモデルを設定し、次にユーザーインターフェースに使用されるウィジェットを設定します。

Window::Window(QWidget *parent)
    : QWidget(parent)
{
    setupModel();

    nameLabel = new QLabel(tr("Na&me:"));
    nameEdit = new QLineEdit();
    addressLabel = new QLabel(tr("&Address:"));
    addressEdit = new QTextEdit();
    typeLabel = new QLabel(tr("&Type:"));
    typeComboBox = new QComboBox();
    nextButton = new QPushButton(tr("&Next"));
    previousButton = new QPushButton(tr("&Previous"));

    nameLabel->setBuddy(nameEdit);
    addressLabel->setBuddy(addressEdit);
    typeLabel->setBuddy(typeComboBox);

「typeid」フィールドに対して設定したリレーションに基づき、メインモデルからコンボボックス用のモデルを取得します。コンボボックスのsetModelColumn() を呼び出すことで、モデル内のフィールドから表示するフィールドが選択されます。

このアプローチは、コンボボックスのモデルを設定するという点で、「Combo Widget Mapper Example」で使用されているものと同様であることに注意してください。ただし、このケースでは、別途モデルを作成するのではなく、QSqlRelationalTableModel 内のリレーションに基づいてモデルを取得します。

次に、各入力ウィジェットをモデル内のフィールドに関連付けるように、ウィジェットマッパーを設定します。

    QSqlTableModel *relModel = model->relationModel(typeIndex);
    typeComboBox->setModel(relModel);
    typeComboBox->setModelColumn(relModel->fieldIndex("description"));

    mapper = new QDataWidgetMapper(this);
    mapper->setModel(model);
    mapper->setItemDelegate(new QSqlRelationalDelegate(this));
    mapper->addMapping(nameEdit, model->fieldIndex("name"));
    mapper->addMapping(addressEdit, model->fieldIndex("address"));
    mapper->addMapping(typeComboBox, typeIndex);

コンボボックスの場合、setupModel() 関数からモデルのフィールドのインデックスはすでにわかっています。マッパーと入力ウィジェットの間のプロキシとしてQSqlRelationalDelegate を使用し、モデルの「typeid」値とコンボボックスのモデルの「typeid」値を照合して、コンボボックスに整数値ではなく説明文を表示させます。

その結果、ユーザーはコンボボックスから項目を選択でき、関連する値がモデルに書き戻されます。

コンストラクタの残りの部分では、接続とレイアウトの設定を行います:

    connect(previousButton, &QPushButton::clicked,
            mapper, &QDataWidgetMapper::toPrevious);
    connect(nextButton, &QPushButton::clicked,
            mapper, &QDataWidgetMapper::toNext);
    connect(mapper, &QDataWidgetMapper::currentIndexChanged,
            this, &Window::updateButtons);

    QGridLayout *layout = new QGridLayout();
    layout->addWidget(nameLabel, 0, 0, 1, 1);
    layout->addWidget(nameEdit, 0, 1, 1, 1);
    layout->addWidget(previousButton, 0, 2, 1, 1);
    layout->addWidget(addressLabel, 1, 0, 1, 1);
    layout->addWidget(addressEdit, 1, 1, 2, 1);
    layout->addWidget(nextButton, 1, 2, 1, 1);
    layout->addWidget(typeLabel, 3, 0, 1, 1);
    layout->addWidget(typeComboBox, 3, 1, 1, 1);
    setLayout(layout);

    setWindowTitle(tr("SQL Widget Mapper"));
    mapper->toFirst();
}

完全を期すために、updateButtons() スロットの実装を示します:

void Window::updateButtons(int row)
{
    previousButton->setEnabled(row > 0);
    nextButton->setEnabled(row < model->rowCount() - 1);
}

まとめと参考資料

コンボボックス用に別のモデルを使用し、ウィジェットマッパー用に特別なデリゲートを定義することで、ユーザーに選択肢のメニューを表示できるようになります。選択肢はユーザーのデータと同じデータベースに格納されますが、別のテーブルに保持されます。このアプローチを用いることで、データベースの機能を適切に活用しつつ、後で完全なレコードを再構築することができます。

SQLモデルを使用していない場合でも、複数のモデルを使用してユーザーに選択肢を表示することは可能です。これについては、「Combo Widget Mapper Example」で解説されています。

サンプルプロジェクト @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.