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

news2025/1/16 16:38:13

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

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

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

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

方法描述
QSqlTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase())构造函数,创建 QSqlTableModel 对象。
setTable(const QString &tableName)设置要操作的数据库表名。
select()执行查询操作,从数据库中获取数据。
rowCount(const QModelIndex &parent = QModelIndex()) const返回模型中的行数。
columnCount(const QModelIndex &parent = QModelIndex()) const返回模型中的列数。
record(int row)返回指定行的记录。
setFilter(const QString &filter)设置用于过滤数据的条件。
setSort(int column, Qt::SortOrder order)设置排序的列和排序规则。
setEditStrategy(QSqlTableModel::EditStrategy strategy)设置编辑策略,决定何时将修改提交到数据库。
setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)设置模型中指定索引的数据。
data(const QModelIndex &index, int role = Qt::DisplayRole) const返回模型中指定索引的数据。
addRecord(const QSqlRecord &values)添加一条记录到模型中。
removeRow(int row)从模型中删除指定行。
insertRecord(int row, const QSqlRecord &record)在指定位置插入一条记录。
submitAll()提交所有对模型的修改到数据库。
revertAll()撤销对模型的所有修改。
setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole)设置表头数据。
indexInQuery(const QSqlQuery &query) const返回查询中模型的索引。

这些方法提供了对 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.4 插入一条记录

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())来实现删除所选行,因为其实现起来很简单此处就不再演示,具体实现细节可以参考附件。

1.2.5 修改表中记录

如下所示代码,用于批量修改表格中所有记录的 “Uage” 字段值为某个固定的年龄。下面是代码的详细解释:

检查是否有记录

如果表格中没有记录,则直接返回,不执行后续的批量修改操作。

if (tabModel->rowCount() == 0)
    return;

循环遍历每一行记录并修改年龄

首先使用 tabModel->record(i) 获取表格模型中的第 i 行记录,接着使用 ui->lineEdit->text() 获取用户在 QLineEdit 中输入的文本,作为新的年龄值,并通过 aRec.setValue("age", ...) 设置 “age” 字段的新值,最后使用 tabModel->setRecord(i, aRec) 将修改后的记录设置回表格模型中的相应行。

for (int i = 0; i < tabModel->rowCount(); i++)
{
    QSqlRecord aRec = tabModel->record(i);                // 获取当前记录
    aRec.setValue("age", ui->lineEdit->text());           // 设置数据,使用 QLineEdit 中的文本作为新的年龄值
    tabModel->setRecord(i, aRec);                         // 将修改后的记录设置回表格模型中的相应行
}

提交修改

使用 tabModel->submitAll() 提交对表格模型的所有修改,将修改保存到数据库中。

tabModel->submitAll();

上述代码实现了一个简单的批量修改操作,将表格中所有记录的 “Uage” 字段值设置为用户在 QLineEdit 中输入的年龄值。请注意,这里没有对输入的年龄值进行验证,确保输入的是合法的数字。在实际应用中,可能需要添加一些输入验证和错误处理的逻辑。

1.2.6 表记录的排序

升序与降序排列

对表中记录的排序可以使用模型提供的setSort函数来实现,通过对该字段第二个参数设置为Qt::AscendingOrder则是升序排序,反之如果设置为Qt::DescendingOrder则为降序排序。

如下所示代码用于根据用户选择的字段对表格进行排序,并重新执行查询以更新表格数据。下面是代码的详细解释:

  • ui->comboBox->currentIndex() 获取用户在 QComboBox 中选择的字段的索引。
  • Qt::AscendingOrder 表示升序排序。
  • tabModel->select()执行对数据库的查询操作,重新获取数据并应用排序。
// 升序排序
tabModel->setSort(ui->comboBox->currentIndex(), Qt::AscendingOrder);
// 降序排序
tabModel->setSort(ui->comboBox->currentIndex(),Qt::DescendingOrder);
// 刷新查询
tabModel->select();

上述代码的作用是根据用户在下拉框中选择的字段进行升序或降序排序,并将排序后的结果重新加载到表格中。在使用这段代码之前,用户需要在 QComboBox 中选择一个字段,作为排序的依据。以升序排序为例,输出效果如下图所示;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1340595.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android 8.1 设置USB传输文件模式(MTP)

项目需求&#xff0c;需要在电脑端adb发送通知手机端接收指令&#xff0c;将USB的仅充电模式更改成传输文件&#xff08;MTP&#xff09;模式&#xff0c;便捷用户在我的电脑里操作内存文件&#xff0c;下面是我们的常见的修改方式 1、android12以下、android21以上是这种方式…

竞赛保研 基于大数据的时间序列股价预测分析与可视化 - lstm

文章目录 1 前言2 时间序列的由来2.1 四种模型的名称&#xff1a; 3 数据预览4 理论公式4.1 协方差4.2 相关系数4.3 scikit-learn计算相关性 5 金融数据的时序分析5.1 数据概况5.2 序列变化情况计算 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &…

机器人中的数值优化之牛顿共轭梯度法

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 本文ppt来自深蓝学院《机器人中的数值优化》 如何解决Hessian矩阵非正定的情况 求解线性系统需要很精确么 引入截断的机制&#xff0c;如果Hessia…

TC3XX GTM时钟频率计算

一、CMU框图 二、TOM 固定时钟生成(FXU)子单元为TOM模块和MON模块生成预定义的不可配置时钟CMU_FXCLK[y](y:0…4)。CMU_FXCLK[y]信号是从全局时钟分频器产生的CMU_GCLK_EN信号中导出的。划分因子被定义为20、24、28、212和216 。 1、相关配置 1.1 GTM外围频率 1.2可配置…

经典目标检测YOLO系列(一)复现YOLOV1(2)反解边界框及后处理

经典目标检测YOLO系列(一)复现YOLOV1(2)反解边界框及后处理 在上个博客&#xff0c;我们提出了新的YOLOV1架构&#xff0c;这次我们解决前向推理过程中的两个问题。 经典目标检测YOLO系列(一)YOLOV1的复现(1)总体架构 1、边界框的计算 1.1 反解边界框公式的改变 1.1.1 原版…

使用pytorch搭建ResNeXt并基于迁移学习训练

冻结除最后全连接层以外的所有权重&#xff0c;只去单独训练它最后一层的的权重&#xff0c;这个方法&#xff0c;冻结了所有网络的权重。 for param in net.parameters():param.requires_grad False

Python武器库开发-武器库篇之Git创建远程仓库和建立SSH key 免密登陆(三十七)

武器库篇之Git创建远程仓库和建立SSH key 免密登陆(三十七) Git是一种版本控制系统&#xff0c;用于跟踪文件的更改和协调多人开发项目。它可以记录文件的历史更改&#xff0c;协助多人协作开发&#xff0c;并提供分支管理功能。Git是一个分布式系统&#xff0c;意味着每个人在…

Docker自建文件快递柜系统

Docker自建文件快递柜系统。 软件特色&#xff1a; 轻量简洁&#xff1a;FastapiSqlite3Vue2ElementUI 轻松上传&#xff1a;复制粘贴&#xff0c;拖拽选择 多种类型&#xff1a;文本&#xff0c;文件 防止爆破&#xff1a;错误次数限制 防止滥用&#xff1a;IP限制上传次数…

【CSAPP】探究BombLab奥秘:Phase_1的解密与实战

&#x1f4cb; 前言 ​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《斯坦福大学之CSAPP》⏰诗赋清音&#xff1a;桃花灼灼春风暖&#xff0c;心随乐曲扬徐徐。 苦尽甘来梦未阑&#xff0c;岁月长河任舟游。 ​ &#x1f389;欢迎…

【C++篇】讲解string容器及其操作

文章目录 &#x1f354;简述string容器⭐字符串拼接操作⭐查找和替换⭐字符串比较⭐插入和删除⭐获取字串 &#x1f354;简述string容器 在C STL中&#xff0c;string是一个字符串容器&#xff0c;它封装了字符串相关的操作&#xff0c;提供了很多方便的方法来处理字符串。 具…

k8s搭建(三、k8s从节点创建)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

完全背包问题,原理剖析,公式推导,OJ详解

文章目录 前言一、完全背包的状态设计1、状态设计2、状态转移方程3、对比0/1背包问题4、时间复杂度分析 二、完全背包问题的优化1、时间复杂度优化2、空间复杂度优化 三、OJ练习裸题完全背包离散化最小值 前言 完全背包问题&#xff0c;相比0/1背包问题&#xff0c;实就每个物品…

元道经纬相机信息化赋能光伏电站运维管理

近年来&#xff0c;我国光伏产业高速发展&#xff0c;尤其以分布式光伏发电项目增长迅速&#xff0c;为更好服务新能源发电&#xff0c;大力推广电能替代。与此同时&#xff0c;电力企业亟需改变落后的管理模式&#xff0c;借助信息化软件提升管理效率。 为了进一步提升光伏电…

C++ 一个有bug的贪吃蛇。。。。。。。。

C 一个有bug的贪吃蛇。。。。。。。。 #include <graphics.h> #include<Windows.h> #include<Mmsystem.h> #include<conio.h> #include<time.h> #include<stdio.h> #include<easyx.h> using namespace std; #pragma warning(di…

百度飞桨文心生态成果最新披露:开发者达1070万 模型数超86万

前言 12月28日&#xff0c;由深度学习技术及应用国家工程研究中心主办的WAVE SUMMIT深度学习开发者大会2023在北京召开。百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰现场公布了飞桨文心五载十届最新生态成果&#xff0c;文心一言最新用户规模破1亿&#xf…

Python 爬取 哔站视频弹幕 并实现词云图可视化

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境介绍: python 3.8 解释器 pycharm 编辑器 第三方模块: requests >>> pip install requests protobuf >>> pip install protobuf 如何安装python第三方模块: win R 输入 cmd 点击确定, 输入安装命…

UEFI模拟环境搭建——windows+EDKII

目录 0 说明 1 安装软件 1.1 VS2019的安装 1.2 Python的安装 1.3 IASL的安装 1.4 NASM的安装 1.5 git的下载 2 EDKII的下载 3 配置环境 0 说明 个人感觉UEFI的环境搭建非常复杂&#xff0c;在经过很长一段折磨后&#xff0c;终于还是搭建成功&#xff0c;写下来记录一…

Jenkins实战指南:实现自动化代码拉取和Harbor私有镜像仓库上传

前两篇中&#xff0c;介绍了如何搭建个人私有镜像仓库Harbor&#xff0c;并编写了一个goweb项目进行打包上传&#xff0c;本文则继续介绍如何利用jenkins 实现自动打包镜像并上传。 《云原生必备工具&#xff01;手把手教你搭建Harbor私有仓库(上)》 搭建jenkins mkdir jenk…

二叉树简单题|对称、翻转、合并二叉树

二叉树简单题|对称、翻转、合并二叉树 文章目录 二叉树简单题|对称、翻转、合并二叉树模板代码101 对称二叉树226 翻转二叉树617 合并二叉树 模板代码 private boolean process(TreeNode p, TreeNode q) {if (p null && q null) {return ;} else if (p null &&…