专注于产品开发平台解决方案

C++ Qt开发:SqlTableModel映射组件应用
分享:

Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍SqlTableModule组件的常用方法及灵活运用。

在多数情况下我们需要使用SQL的方法来维护数据库,但此方式相对较为繁琐对于表格等数据的编辑非常不友好,在Qt中提供了QSqlTableModel模型类,它为开发者提供了一种直观的方式来与数据库表格进行交互。通过使用该组件可以将数据库与特定的组件进行关联,一旦关联被建立那么用户的所有操作均可以使用函数的方式而无需使用SQL语句,该特性有点类似于ORM对象关系映射机制。

在接下来的章节中,我们将学习如何配置 QSqlTableModel、与数据库进行交互、实现数据的动态显示和编辑,首先读者应绘制好UI界面,本次案例界面稍显复杂,读者可自行完成如下案例的绘制。

以下是 QSqlTableModel 类的一些常用方法,包括方法名、参数以及简要说明。这里列举的方法并非全部,而是一些常见的方法,更详细的信息可以参考官方文档。

这些方法提供了对 QSqlTableModel 进行数据操作、过滤、排序以及提交修改的基本手段。通过这些方法,可以在应用程序中方便地操作数据库表格的数据。

1.1 初始化组件

首先我们来看一下MainWindow初始化部分是如何工作的,主要实现了以下功能:

打开数据库

首先使用SQLite数据库驱动连接名为"database.db"的数据库文件。如果数据库连接失败,函数直接返回。接着通过新建一个QSqlTableModel类,并调用setTable来打开一个数据表,设置编辑策略为 OnManualSubmit,即手动提交修改。并通过setSort函数来设置排序方式为根据ID字段升序Qt::AscendingOrder排列。

DB = QSqlDatabase::addDatabase("QSQLITE");DB.setDatabaseName("./database.db");if (!DB.open()){
    return;}tabModel = new QSqlTableModel(this, DB);tabModel->setTable("Student");tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);tabModel->setSort(tabModel->fieldIndex("id"), Qt::AscendingOrder);if (!(tabModel->select())){
    return;}

设置字段名称

此处我们数据库中有6个字段,也就需要设置数据库字段与表格关联,如下则是对字段的动态关联。

tabModel->setHeaderData(tabModel->fieldIndex("id"),Qt::Horizontal,"Uid");
tabModel->setHeaderData(tabModel->fieldIndex("name"),Qt::Horizontal,"Uname");
tabModel->setHeaderData(tabModel->fieldIndex("sex"),Qt::Horizontal,"Usex");
tabModel->setHeaderData(tabModel->fieldIndex("age"),Qt::Horizontal,"Uage");
tabModel->setHeaderData(tabModel->fieldIndex("mobile"),Qt::Horizontal,"Umobile");
tabModel->setHeaderData(tabModel->fieldIndex("city"),Qt::Horizontal,"Ucity");

关联选择模型和数据模型

通过创建 QItemSelectionModel 对象 theSelection 并关联到 tabModel模型,将数据模型和选择模型关联到 ui->tableView,并设置选择模式为行选择模式。

theSelection = new QItemSelectionModel(tabModel);
ui->tableView->setModel(tabModel);
ui->tableView->setSelectionModel(theSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);

创建数据映射

创建 QDataWidgetMapper 对象 dataMapper,将数据模型设置为 tabModel,设置提交策略为 AutoSubmit,即自动提交修改。并将 "name" 字段映射到 ui->lineEdit_name,默认选中第一条映射记录。

dataMapper = new QDataWidgetMapper();
dataMapper->setModel(tabModel);
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
dataMapper->addMapping(ui->lineEdit_name, tabModel->fieldIndex("name"));
dataMapper->toFirst();

信号和槽连接

当选择模型中的当前行改变时,连接到槽函数 on_currentRowChanged,用于在右侧编辑框中输出当前选择的记录。

connect(theSelection, SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(on_currentRowChanged(QModelIndex, QModelIndex)));

这个槽函数的实现如下所示,当行被点击后执行获取name/mobile字段,并放入映射数据集中的lineEdit编辑框中,使其能够动态的显示数据列表。

void MainWindow::on_currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
{
    Q_UNUSED(previous);
    dataMapper->setCurrentIndex(current.row());      // 更细数据映射的行号
    int curRecNo=current.row();                      // 获取行号
    QSqlRecord  curRec=tabModel->record(curRecNo);   // 获取当前记录
    QString uname = curRec.value("name").toString();     // 取出数据
    QString mobile = curRec.value("mobile").toString();
    ui->lineEdit_name->setText(uname);                   // 设置到编辑框
    ui->lineEdit_mobile->setText(mobile);
}

最后在UI文件的底部有一个comboBox组件,我们通过动态的查询记录,并将其赋值为第一个字段元素,其代码如下所示:

QSqlRecord emptyRec=tabModel->record();           //获取空记录,只有字段名
for (int i=0;i<emptyRec.count();i++)
{
    ui->comboBox->addItem(emptyRec.fieldName(i));
}

这段代码实现了一个简单的数据库浏览和编辑界面,用户可以通过表格展示的方式查看和编辑 "Student" 表格中的数据。当程序运行后则可以看到如下图所示的初始化部分:

1.2 数据处理

1.2.1 新增一条记录

当用户按下on_pushButton_add_clicked按钮时,则会在表格中新增一条记录,并设置默认值的功能。下面是代码的详细解释:

插入新行

在表格模型 tabModel 的末尾插入一行新记录。QModelIndex() 是一个空的索引,表示插入到末尾。

tabModel->insertRow(tabModel->rowCount(), QModelIndex());

获取最后一行的索引

获取刚刚插入的行的索引,这里假设 "name" 字段对应的列索引是 1。

QModelIndex curIndex = tabModel->index(tabModel->rowCount() - 1, 1);

清空选择项并设置新行为当前选择行

清空当前选择项,然后将刚刚插入的行设为当前选择行,并选择该行。

theSelection->clearSelection();

theSelection->setCurrentIndex(curIndex, QItemSelectionModel::Select);

获取当前行号

获取当前行的行号。

int currow = curIndex.row();

设置自动生成的编号和默认值

这段代码的作用是在表格模型中插入一行新记录,然后设置该行的默认值,其中 "Uid" 字段会自动生成一个编号,"Usex" 字段默认为 "M","Uage" 字段默认为 "0"。

  • 自动生成编号,假设 "Uid" 字段对应的列索引是 0。

  • 将 "Usex" 字段设置为 "M"。

  • 将 "Uage" 字段设置为 "0"。

tabModel->setData(tabModel->index(currow, 0), 1000 + tabModel->rowCount());

tabModel->setData(tabModel->index(currow, 2), "M");

tabModel->setData(tabModel->index(currow, 3), "0");

运行代码,读者可自行点击增加记录按钮,每次点击均会在表格中提供新行,当读者点击on_pushButton_save_clicked保存按钮是则会调用submitAll()该函数用于将数据提交到数据库中存储,如下图所示:

1.2.2 插入一条记录

在 TableView 中当前选择行的上方插入一行新记录,并自动生成编号。下面是代码的详细解释:

获取当前选择行的索引和行号

获取当前选择的单元格的索引和行号。

QModelIndex curIndex = ui->tableView->currentIndex();

int currow = curIndex.row();

在当前行上方插入一行新记录

在表格模型 tabModel 的当前选择行(curIndex.row())的上方插入一行新记录。QModelIndex() 是一个空的索引,表示插入到指定行的上方。

tabModel->insertRow(curIndex.row(), QModelIndex());

设置自动生成的编号

自动生成编号,假设 "Uid" 字段对应的列索引是 0。

tabModel->setData(tabModel->index(currow, 0), 1000 + tabModel->rowCount());

清除已有选择并将当前选择行设为新插入的行

清空已有选择项,然后将当前选择行设为新插入的行,并选择该行。

theSelection->clearSelection();

theSelection->setCurrentIndex(curIndex, QItemSelectionModel::Select);

当上述代码运行后则可以实现在指定行的上方插入一行新纪录,并为新插入的行生成一个自增的编号,其效果如下图所示:

对于删除一条记录来说则可以通过调用tabModel->removeRow(curIndex.row())来实现删除所选行,因为其实现起来很简单此处就不再演示,具体实现细节可以参考附件。

行业痛点
解决方案
应用案例