SQL 小工具映射器示例

SQL Widget 映射器示例展示了如何将数据库中的信息映射到表单上的 widget。

组合部件映射器示例中,我们展示了如何在部件映射器和具有特殊用途模型的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 数据库,其中包含一个带有主键、姓名、地址和类型字段的 "人 "表。

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();
}

人 "表中的 "typeid "字段通过QSqlRelationalTableModel 中的关系与 "addresstype "表中的内容相关联。这种模型完成了在数据库中存储数据的所有必要工作,也允许将任何关系本身作为模型使用。

在本例中,我们为 "person "表中的 "typeid "字段定义了一个关系,将其与 "addresstype "表 中的 "id "字段相关联,并在向用户显示 "typeid "时使用 "description "字段的内容。(详见QSqlRelationalTableModel::setRelation() 文档)。

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() 时,我们会选择模型中要显示的字段。

请注意,这种方法与Combo Widget Mapper 示例中使用的方法类似,我们为组合框设置了一个模型。不过,在本例中,我们是根据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 示例就涵盖了这一点。

示例项目 @ code.qt.io

© 2025 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.