本页内容

SQL 控件映射器示例

SQL 控件映射器示例演示了如何将数据库中的信息映射到表单上的控件。

主窗口显示用户名、用户地址,并标注地址类型。

在“下拉列表控件映射器示例”中,我们演示了如何利用控件映射器与QComboBox 控件之间的命名映射,并借助一个专用模型,将模型中的值与选项列表相关联。

同样,我们创建了一个Window 类,其用户界面几乎完全相同,提供了一个下拉列表框,允许将地址分类为“家庭”、“工作”或“其他”。但是,我们没有使用单独的模型来保存这些地址类型,而是使用一个数据库表来保存示例数据,另一个数据库表来保存地址类型。 通过这种方式,我们将所有信息存储在同一个地方。

窗口类定义

该类提供了一个构造函数、一个用于保持按钮状态同步的槽,以及一个用于设置模型的私有函数:

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() 函数中进行初始化。在此,我们创建了一个SQLite数据库,其中包含一个名为“person”的表,该表具有主键、姓名、地址和类型字段。

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)");

在表的每一行中,我们为这些字段插入默认值,包括与地址类型对应的值——这些地址类型存储在另一张表中。

主窗口显示了一个表格,其中包含数据库中存储的信息。

我们创建了一个名为“addresstype”的表,其中包含“person”表中使用的标识符及其对应的字符串:

    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 类的构造函数可分为三个部分来解释。在第一部分,我们设置用于存储数据的模型,然后设置用于用户界面的控件:

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()方法,会选定模型中该字段以供显示。

请注意,这种方法与“下拉列表控件映射器示例”中的做法相似,即我们为下拉列表创建了一个模型。不过,在此情况下,我们是基于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”值与下拉列表模型中的对应值进行匹配,并向下拉列表中填充描述文本而非整数值。

因此,用户可以从下拉列表中选择一个项目,相关值会被写回模型。

构造函数的其余部分用于设置连接和布局:

    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 模型,仍然可以使用多个模型向用户呈现选项。这一点在“下拉列表控件映射器示例”中有所涉及。

示例项目 @ 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.