C++ Qt开发:数据库与TableView多组件联动
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍TableView
组件与数据库联动的常用方法及灵活运用。
在Qt中,通常我们不会在TableView
等组件中保存数据,一般会将这些数据存储至数据库或者是文件中保存,当使用时则动态的在数据库中调出来,以下案例将实现,当用户点击并选中TableView
组件内的某一行时,我们通过该行中的name
字段查询,并将查询结果关联到ListView
组件内,同时将TableView
中选中行的字段分别显示在窗体底部的LineEdit
编辑框内。
要实现联动涉及几个主要步骤:建立数据库连接、创建模型、设置TableView
、捕捉TableView
的选中信号、查询并关联数据、更新LineEdit
和ListView
,首先我们在UI
界面中绘制所需控件,如下图左侧放一个TableView
组件,右侧是一个ListView
组件,底部放三个LineEdit
组件;
接着我们需要创建两张数据表,其中Student
表主要用来存储学生信息,而StudentAddressList
用于存储学生所管理的IP地址,我们将表中的name
进行关联,每个学生名下存储有不同的地址;
创建两个表结构总结起来代码如下所示,通过分别调用多次db.exec()
函数实现创建数据表,并通过QSqlQuery
类实现批量插入数据集;
void InitMultipleSQL()
{
// ----------------------------------------------
// 初始化表结构
// ----------------------------------------------
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("./database.db");
if (!db.open())
{
std::cout << db.lastError().text().toStdString()<< std::endl;
return;
}
// 执行SQL创建表
db.exec("DROP TABLE Student");
db.exec("CREATE TABLE Student ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(40) NOT NULL, "
"age INTEGER NOT NULL)"
);
// 批量创建数据
QStringList name_list; name_list << "lyshark" << "admin" << "guest";
QStringList age_list; age_list << "25" << "34" << "45";
// 绑定并插入数据
QSqlQuery query;
query.prepare("INSERT INTO Student(name,age) ""VALUES (:name, :age)");
if(name_list.size() == age_list.size())
{
for(int x=0;x< name_list.size();x++)
{
query.bindValue(":name",name_list[x]);
query.bindValue(":age",age_list[x]);
query.exec();
}
}
// ----------------------------------------------
// 创建第二张表
// ----------------------------------------------
// 与第一张表通过姓名关联起来
db.exec("DROP TABLE StudentAddressList");
db.exec("CREATE TABLE StudentAddressList("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(40) NOT NULL, "
"address VARCHAR(128) NOT NULL"
")");
// 插入数据
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('lyshark','192.168.1.1')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('lyshark','192.168.1.2')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('lyshark','192.168.1.3')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('admin','192.168.10.10')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('admin','192.168.10.11')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('guest','192.168.100.100')");
}
1.1 初始化组件
接着我们需要在构造函数MainWindow::MainWindow(QWidget *parent)
内初始化``TableView表格,查询
Student表内记录,将查询到的指针绑定到
theSelection模型上,绑定后再将绑定指针加入到
dataMapper组件映射中,即可实现初始化,这里有必要介绍一下
QSqlQueryModel、
QItemSelectionModel、
QDataWidgetMapper`这三个模型类。
QSqlQueryModel
用于与数据库交互的模型类之一,它继承自 QAbstractTableModel
。QSqlQueryModel
通过执行 SQL 查询语句,将查询结果作为表格数据提供给 Qt 的视图组件,如 QTableView
等。
以下是 QSqlQueryModel
的一些常用方法,概述成表格形式:
方法 | 描述 |
---|---|
setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase()) |
设置要执行的 SQL 查询和数据库连接。查询执行后,结果将被提供给模型。 |
clear() |
清除模型中的数据。 |
lastError() const |
返回最后一次执行的查询的错误。 |
record() const |
返回包含查询结果字段信息的 QSqlRecord 对象。 |
data(const QModelIndex &item, int role = Qt::DisplayRole) const |
返回与给定索引处的项相关联的数据,用于提供给视图请求的数据。 |
rowCount(const QModelIndex &parent = QModelIndex()) const |
返回模型中的行数。 |
columnCount(const QModelIndex &parent = QModelIndex()) const |
返回模型中的列数。 |
上述方法提供了一般性的查询执行、错误处理、结果处理等功能,使得通过 QSqlQueryModel
能够方便地将数据库中的查询结果集与 Qt 的视图组件进行关联。使用这些方法,你可以在应用中执行 SQL 查询,并将结果显示在相应的视图组件中。
QItemSelectionModel
用于管理项选择的模型类,它是 QAbstractItemModel
类的衍生类。QItemSelectionModel
用于追踪一个或多个视图中的选择项,同时允许对这些选择项进行查询和修改。
以下是 QItemSelectionModel
的一些常用方法,概述成表格形式:
方法 | 描述 |
---|---|
QItemSelectionModel(QAbstractItemModel *model) |
构造函数,创建一个选择模型并关联指定的数据模型。 |
setModel(QAbstractItemModel *model) |
设置关联的数据模型。 |
model() const |
返回与此选择模型相关联的数据模型。 |
currentIndex() const |
返回当前焦点的项的索引。 |
selectedIndexes() const |
返回当前选择的项的索引列表。 |
clear() |
清除模型中的所有选择项。 |
select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) |
根据给定的 QModelIndex 对象和选择标志执行选择操作。 |
select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) |
根据给定的 QItemSelection 对象和选择标志执行选择操作。 |
reset() |
重置选择模型,清除所有选择和当前索引。 |
setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) |
设置当前焦点项。 |
currentIndexChanged(const QModelIndex ¤t, const QModelIndex &previous) |
当前焦点项变化时发出的信号。 |
selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) |
选择发生变化时发出的信号。 |
这些方法允许你在一个或多个视图中管理选择项,进行选择的查询、修改,以及处理选择变化的信号。通过使用这些方法,你可以实现对模型中的项进行灵活的选择操作,并及时响应选择的变化。
QDataWidgetMapper
用于实现数据和小部件之间双向映射的类,使得数据模型的变化能够反映在界面上,同时用户界面的修改也能够同步到数据模型中。
以下是 QDataWidgetMapper
的一些主要方法,概述成表格形式:
方法 | 描述 |
---|---|
QDataWidgetMapper(QObject *parent = nullptr) |
构造函数,创建一个数据映射器对象。 |
setModel(QAbstractItemModel *model) |
设置映射的数据模型。 |
model() const |
返回与此数据映射器相关联的数据模型。 |
addMapping(QWidget *widget, int section) |
将指定的小部件和数据模型的字段进行映射。 |
removeMapping(QWidget *widget) |
移除与指定小部件的映射关系。 |
mappedWidgetAt(int section) const |
返回与数据模型的指定字段映射的小部件。 |
mappedSection(QWidget *widget) const |
返回与指定小部件映射的数据模型字段的索引。 |
setCurrentIndex(int index) |
将映射的数据移动到指定的索引。 |
currentIndex() const |
返回当前映射的数据的索引。 |
toFirst(), toLast(), toNext(), toPrevious() |
分别将映射的数据移动到第一行、最后一行、下一行、上一行。 |
submit() |
将界面上的更改提交到模型。 |
revert() |
撤销所有未提交的更改。 |
setSubmitPolicy(QDataWidgetMapper::SubmitPolicy policy) |
设置更改提交策略。 |
submitPolicy() const |
返回当前的更改提交策略。 |
addMapping(QWidget *widget, int section, const QByteArray &propertyName) |
将小部件和数据模型字段以及小部件属性进行映射。 |
toFirst(), toLast(), toNext(), toPrevious() |
分别将映射的数据移动到第一行、最后一行、下一行、上一行。 |
这些方法使得在 Qt 应用程序中更容易实现数据模型和用户界面的交互,通过将数据模型字段映射到用户界面的小部件上,实现了数据的显示和编辑的同步。通过调用 setCurrentIndex
、toNext
、toPrevious
等方法,你可以在数据模型中移动,并自动更新映射的小部件上显示的数据。最后,通过调用 submit
将界面上的更改提交到模型,而 revert
则撤销未提交的更改。
初始化UI界面很容易实现,首席按初始化表结构,通过调用封装好的InitMultipleSQL
可以直接初始化并将数据保存至database.db
文件中,在主程序中我们一次执行如下操作来实现数据的初始化与展现。
打开数据库
使用 SQLite 数据库,并尝试打开名为 "database.db" 的数据库文件。如果打开失败,将输出错误信息并返回。需要注意确保数据库文件存在且可访问。
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("./database.db");
if (!db.open())
{
std::cout << db.lastError().text().toStdString()<< std::endl;
return;
}
查询数据表中记录
通过 QSqlQueryModel
查询 Student 表中的所有记录,并按 id 排序。如果查询过程中出现错误,需要处理错误。
qryModel=new QSqlQueryModel(this);
qryModel->setQuery("SELECT * FROM Student ORDER BY id");
设置 TableView 表头数据
设置查询模型的表头数据,分别为 "ID"、"Name"、"Age"。
qryModel->setHeaderData(0,Qt::Horizontal,"ID");
qryModel->setHeaderData(1,Qt::Horizontal,"Name");
qryModel->setHeaderData(2,Qt::Horizontal,"Age");
绑定数据到模型和 TableView
创建一个 QItemSelectionModel
对象 theSelection
,并将其绑定到查询模型 qryModel
上。然后将模型和选择模型分别绑定到 ui->tableView
上,设置选择行为为按行选择。
theSelection=new QItemSelectionModel(qryModel);
ui->tableView->setModel(qryModel);
ui->tableView->setSelectionModel(theSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
创建数据映射器 QDataWidgetMapper
创建 QDataWidgetMapper
对象 dataMapper
,设置提交策略为自动提交。然后将映射器和模型绑定,并将三个文本框小部件与模型的相应字段进行映射。最后,将映射器移动到第一行。
dataMapper= new QDataWidgetMapper();
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
dataMapper->setModel(qryModel);
dataMapper->addMapping(ui->lineEdit_id,0);
dataMapper->addMapping(ui->lineEdit_name,1);
dataMapper->addMapping(ui->lineEdit_age,2);
dataMapper->toFirst();
绑定信号
连接 theSelection
的 currentRowChanged
信号到槽函数 on_currentRowChanged
。这样,当用户在表格中选择不同行时,将触发槽函数执行相应的操作。
connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));
上述代码实现了组件初始化,使用数据库表格中的数据填充了一个 QTableView
,并通过 QDataWidgetMapper
将选中行的数据映射到三个文本框中,同时通过信号槽机制实现了在底部编辑框中显示当前选中行的功能。
1.2 绑定事件
接着我们需要绑定TableView
表格的on_currentRowChanged()
事件,当用户点击TableView
表格中的某个属性时则自动触发该函数,在此函数内我们完成对其他组件的填充,如下是对绑定事件的具体分析。
如下这部分代码使用了 Q_UNUSED
宏,用于标记 previous
未使用,以避免编译器产生未使用变量的警告。接着判断 current
是否有效,如果无效则直接返回,避免出现错误。
Q_UNUSED(previous);
if (!current.isValid())
{
return;
}
dataMapper->setCurrentModelIndex(current);
这段代码判断当前行是否为表格的第一行或最后一行,并输出相应的信息。
// 获取到记录开头结尾
bool first=(current.row()==0); // 是否首记录
bool last=(current.row()==qryModel->rowCount()-1);// 是否尾记录
std::cout << "IsFirst: " << first << "IsLast: " << last << std::endl;
获取当前选择行的 name
字段的数据,并输出到标准输出流。
// 获取name字段数据
int curRecNo=theSelection->currentIndex().row(); // 获取当前行号
QSqlRecord curRec=qryModel->record(curRecNo); // 获取当前记录
QString uname = curRec.value("name").toString();
std::cout << "Student Name = " << uname.toStdString() << std::endl;
代码查询名为 StudentAddressList
的表中与当前用户名匹配的所有数据,并将 address
字段的数据提取出来存储在 the_data
容器中。
// 查StudentAddressList表中所有数据
// 根据姓名过滤出该用户的所有数据
QSqlQuery query;
query.prepare("select * from StudentAddressList where name = :x");
query.bindValue(":x",uname);
query.exec();
// 循环获取该用户的数据,并将address字段提取出来放入QStringList容器
QSqlRecord rec = query.record();
QStringList the_data;
while(query.next())
{
int index = rec.indexOf("address");
QString data = query.value(index).toString();
std::cout << "User Address = " << data.toStdString() << std::endl;
the_data.append(data);
}
最后,将 the_data
中的数据关联到 QListView
控件上,并设置为不可编辑。这样,用户就可以在 QListView
中看到与当前表格行对应的地址信息。
// 关联到ListView数据表中
QStringListModel *model;
model = new QStringListModel(the_data);
ui->listView->setModel(model);
ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
至此核心功能的实现就结束了,通过对信号的绑定,当读者运行程序并选中TableView
组件中的任意一行是,其右侧ListView
与底部的LineEdit
编辑框均会实现联动效果,如下图所示;
C++ Qt开发:数据库与TableView多组件联动的更多相关文章
- C/C++ Qt 数据库与TableView多组件联动
Qt 数据库组件与TableView组件实现联动,以下案例中实现了,当用户点击并选中TableView组件内的某一行时,我们通过该行中的name字段查询并将查询结果关联到ListView组件内,同时将 ...
- Qt大型工程开发技术选型Part3:Qt调用C#编写的COM组件实例
Qt大型工程开发技术选型Part3:Qt调用C#编写的COM组件实例以及错误总结 ok,前面铺垫了那么多,现在来写一个开发实例,我会把其中隐藏的坑和陷阱简单谈谈,并在文章最后总结. 不愿意看长篇大论的 ...
- win使用MSYS2安装Qt开发环境
原文链接 MSYS2 下载地址: pacman的具体用法 有pacman的具体使用方法.我们首先对系统升级 我们首先对系统升级 pacman -Syu 就会检测整个系统可以升级的组件,并自动下载安装, ...
- IOS开发数据库篇—SQLite模糊查询
IOS开发数据库篇—SQLite模糊查询 一.示例 说明:本文简单示例了SQLite的模糊查询 1.新建一个继承自NSObject的模型 该类中的代码: // // YYPerson.h // 03- ...
- 细数Qt开发的各种坑(欢迎围观)
1:Qt的版本多到你数都数不清,多到你开始怀疑人生.从4.6开始到5.8,从MSVC编译器到MINGW编译器,从32位到64位,从Windows到Linux到MAC.MSVC版本还必须安装对应的VS2 ...
- 基于QT开发的第三方库
基于Qt开发的第三方库 分类: Qt2014-02-12 11:34 1738人阅读 评论(0) 收藏 举报 QT第三方库 目录(?)[+] 文章来源:http://blog.csdn.net ...
- SNF快速开发平台MVC-高级查询组件
1. 高级查询 在我们做项目的时候经常想要按名称.编号进行查询数据,可在开发时会把最常用的查询条件写上,不常用的就不写了,也是因为把所有字段都写上太多了,布局不好看而且不实用.还有些查询条件几百年 ...
- SNF快速开发平台MVC-自由排序组件
1. 自由排序功能使用 在一些需要排序优先级的数据进行调整处理,如民族数据,在北方实施的时候汉族比较多,希望把汉族放在第一位.在蒙古实施项目时,蒙古族人最多把蒙古族放在第一选择位. 1.1. ...
- 【应用笔记】【AN005】Qt开发环境下基于RS485的4-20mA电流采集
简介 4-20mA电流环具有广泛的应用前景,在许多行业中都发挥着重要作用.本文主要介绍在Qt开发环境下基于RS485实现4-20mA电流采集,实现WINDOWS平台对数据的采集.分析及显示. 系统组成 ...
- IOS开发-数据库总结
关于数据存储概念: 数据结构: 基本对象:NSDictionary.NSArray和NSSet这些对象. 复杂对象:关系模型.对象图和属性列表多种结构等. 存储方式: 内存:内存存储是临时的,运行时有 ...
随机推荐
- mit6.s081 lab1:Unix Utilities
1 sleep(easy) 要求:为 xv6实现 UNIX 程序睡眠; 睡眠需要暂停一段用户指定的时间.刻度是由 xv6内核定义的时间概念,即定时器芯片两次中断之间的时间.解决的程序应该在 user/ ...
- 生成学习全景:从基础理论到GANs技术实战
本文全面探讨了生成学习的理论与实践,包括对生成学习与判别学习的比较.详细解析GANs.VAEs及自回归模型的工作原理与结构,并通过实战案例展示了GAN模型在PyTorch中的实现. 关注TechLea ...
- OS | 进程和线程基础知识全家桶图文详解✨
前言 先来看看一则小故事 我们写好的一行行代码,为了让其工作起来,我们还得把它送进城(进程)里,那既然进了城里,那肯定不能胡作非为了. 城里人有城里人的规矩,城中有个专门管辖你们的城管(操作系统),人 ...
- SCA技术进阶系列(三):浅谈二进制SCA在数字供应链安全体系中的应用
数字经济时代,随着开源应用软件开发方式的使用度越来越高,开源组件逐渐成为软件开发的核心基础设施,但同时也带来了一些风险和安全隐患.为了解决这些问题,二进制软件成分分析技术成为了一种有效的手段之一.通过 ...
- 五、java操作redis
系列导航 一.redis单例安装(linux) 二.redis主从环境搭建 三.redis集群搭建 四.redis增加密码验证 五.java操作redis --demo主方法 package com. ...
- 双非本科拿下oppo sp!这位粉丝太强了!
哈喽,大家好,我是仲一.今天分享的是一位双非本科生拿下oppo sp的秋招经验.当时,这位粉丝咨询我offer选择的时候,看到年薪31W这个数字,我以为他是研究生.后来,再三确认了,他确实是本科生. ...
- 通过dockerfile构建微服务的镜像发布
本文为博主原创,未经允许不得转载: 目录: 1. dockerfile 的文件使用讲解 2. dockerfile 常用指令 3. 通过dockerfile 进行微服务发布 1. dockerfile ...
- 【C++】在搞touchgfx时遇见了一个初始化列表顺序与类中定义不一致的问题,error:when initialized here [-Werror=reorder]
在搞touchgfx时遇见了一个初始化列表顺序与类中定义不一致的问题,error:when initialized here [-Werror=reorder] 初始化列表顺序与类中定义顺序不一致错误 ...
- jenkins构建报错: Send build artifacts over SSH' changed build result to UNSTABLE
原因包括: ssh配置的用户没有相关的权限. 最好是配置root用户
- MyBatis_问题解决:Invalid bound statement (not found)
Invalid bound statement (not found)问题,即在mybatis中dao接口与mapper配置文件在做映射绑定的时候出现问题,简单说,就是接口与xml要么是找不到,要么是 ...