目录
前言
Qt 作为跨平台 C++ 框架,自带 SQLite 驱动与 Model/View 架构,理论上可以不写一行 SQL就可完成增删改查等基础任务。但官方文档分散、示例过于复杂,不能满足参考需求,真正要把 QML TableView 展示、模型层增删改、数据库落库 串成闭环,往往要踩不少坑。
本篇文章带着 能跑起来的最小示例 思路给出参考示例: 从数据库自动创建、模型封装、QML 双向绑定,到新增/修改/删除/立即落库,一气呵成。
相关阅读
QML TableView:基础用法和自定义样式实现
QML TableView:实现可排序与可编辑的表格组件
效果演示
1.启动后自动创建 people.db
,并在 TableView 中列出所有记录(初始为空,数据是预先存入的)。

2.点击新增,在输入框可填写 name、age,点击"确定"按钮时写入模型,TableView同步刷新(id默认为0)。

3.单击表格任意行 → 点击"修改"按钮→ 该行数据回填到输入框 → 修改后点"确定"按钮 → 写入模型、TableView显示同步。

4.选中行后点"删除",该行从模型与界面同步消失。

5.点击"保存"按钮,将模型数据同步至数据库,TableView中ID会同步更新。

代码示例(初始化)
cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "sqltablemodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
/* 1. 建库/建表,只做一次 */
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("people.db");
if (!db.open()) qFatal("open db error");
db.exec("CREATE TABLE IF NOT EXISTS people ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT, "
"age INTEGER)");
/* 2. 实例化模型并注册 */
SqlTableModel *model = new SqlTableModel(&app);
QQmlApplicationEngine engine;
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
// 注册后,sqlModel 可在 QML中使用
engine.rootContext()->setContextProperty("sqlModel", model);
engine.loadFromModule("qml_tableview_sqlite", "Main");
return app.exec();
}
代码说明
- 使用
QSQLITE
驱动,本地文件people.db
不存在时会自动创建。 CREATE TABLE IF NOT EXISTS
保证重复启动不会重复建表。- 将
SqlTableModel
实例以sqlModel
名字注入 QML,供前端直接调用增删改接口。
代码示例(模型)
下面这段代码是一个基于 Qt 框架的 C++ 类,名为 SqlTableModel
,它继承自 QSqlTableModel
,用于简化对数据库表 people
的增删改查操作。
sqltablemodel.h
cpp
#ifndef SQLTABLEMODEL_H
#define SQLTABLEMODEL_H
#include <QSqlTableModel>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QList>
#include <QDebug>
class SqlTableModel : public QSqlTableModel
{
Q_OBJECT
public:
explicit SqlTableModel(QObject *parent = nullptr);
// 增加数据 name age
Q_INVOKABLE void appendRow(const QString &name, int age);
// 删除选中行数据
Q_INVOKABLE void removeRow(int row);
// 修改
Q_INVOKABLE void editRow(int row, const QString &name, int age);
// 获取指定单元格数据
Q_INVOKABLE QVariant cell(int row, int col) const;
// 保存
Q_INVOKABLE bool saveAll();
};
#endif // SQLTABLEMODEL_H
这些方法都被标记为
Q_INVOKABLE
,意味着它们可以从 QML 层直接调用
sqltablemodel.cpp 实现:
cpp
#include "sqltablemodel.h"
#include <QSqlRecord>
#include <QSqlField>
SqlTableModel::SqlTableModel(QObject *parent)
: QSqlTableModel(parent)
{
setTable("people");
setEditStrategy(OnManualSubmit); // 手动提交,saveAll() 时一次性写入
select();
}
/* 增 */
void SqlTableModel::appendRow(const QString &name, int age)
{
QSqlRecord r = record(); // 获得表结构
r.setValue("name", name);
r.setValue("age", age);
insertRecord(-1, r); // -1 表示追加到末尾
}
/* 删:只删当前选中行(QML 里把 TableView.selection.currentRow 传进来)*/
void SqlTableModel::removeRow(int row)
{
if (row < 0 || row >= rowCount())
return;
qDebug() << "row = " << row;
// 仅标记删除,真正删除在 submitAll() 或 saveAll()
QSqlTableModel::removeRow(row);
}
/* 改:修改指定行 */
void SqlTableModel::editRow(int row, const QString &name, int age)
{
if (row < 0 || row >= rowCount())
return;
setData(index(row, fieldIndex("name")), name);
setData(index(row, fieldIndex("age")), age);
}
/* 获取指定单元格数据 */
QVariant SqlTableModel::cell(int row, int col) const
{
return data(index(row, col), Qt::DisplayRole);
}
/* 保存:一次性提交所有增删改 */
bool SqlTableModel::saveAll()
{
return submitAll();
}
代码说明
SqlTableModel就是Qt提供的一个"数据库表格模型",它把SQL表的内容读到内存,让QML界面能直接当表格显示。这个类在原始QSqlTableModel基础上又封装了5个简单的接口,方便QML一键调用:
增:appendRow(name, age)
将name、age作为一条新的记录插入到表格末尾,暂存在模型中,由于设置了手动提交,没有及时插入数据库中。
QSqlTableModel提交策略:
提交策略 | 值 | 描述 |
---|---|---|
QSqlTableModel::OnFieldChange |
0 | 所有字段的更改都会立即更新到数据库 |
QSqlTableModel::OnRowChange |
1 | 当用户选择另一行时,对该行做出的更改将同步到数据库 |
QSqlTableModel::OnManualSubmit |
2 | 所有的更改都会缓存到模型中,直至调用 submitAll() 或者 revertAll() |
删:removeRow(row)
删除指定行号的记录,仅标记删除,真正删除在 submitAll()
或调用已封装的保存 saveAll()
接口。
改:editRow(row, name, age)
将指定行的name、age改为新值,由于设置了手动提交,不会更新到数据库中,只会在TableView中体现更改。
查:cell(row, col)
返回指定单元格的文本,供QML读取,在这里用于获取待修改的数据。
保存:saveAll()
将之前所有的增、删、改操作后的数据,一次性写入数据库中,返回是否成功。
代码示例(QML界面)
Main.qml
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window {
id: root
visible: true
width: 600
height: 400
title: "SQLite TableView 示例"
// 属性定义
property int selectedRow: -1
property int hoveredRow: -1
readonly property int columnWidth: width/3 - 5
readonly property int rowHeight: 30
property bool editMode: false
// 颜色定义
readonly property color headerColor: "#2196F3"
readonly property color selectedColor: "#bbdefb"
readonly property color hoverColor: "#e3f2fd"
readonly property color borderColor: "#e0e0e0"
ColumnLayout {
anchors.fill: parent
anchors.margins: 6
// 为表头留出空间
spacing: 40
// 操作栏
RowLayout {
Layout.minimumHeight: 40
Layout.fillWidth: true
Button { text: "新增"; onClicked: addDialog.addData() }
Button { text: "删除"; onClicked: sqlModel.removeRow(selectedRow) }
Button { text: "修改"; onClicked: addDialog.editData() }
Button { text: "保存"; onClicked: sqlModel.saveAll() }
}
TableView {
id: table
// 填满剩余位置
Layout.fillHeight: true
Layout.fillWidth: true
// 表头组件,绑定TableView, 放在表格上方
HorizontalHeaderView {
syncView: table
height: 30
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.top
resizableColumns: true
}
// 单选
selectionMode: TableView.SingleSelection
// 绑定模型
model: sqlModel
// 自定义单元格
delegate: Rectangle {
implicitWidth: columnWidth
implicitHeight: rowHeight
color: row === selectedRow ? selectedColor :
row === hoveredRow ? hoverColor : row % 2 ? "#f5f5f5" : "white"
// 处理鼠标事件
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: selectedRow = row
onEntered: hoveredRow = row
onExited: hoveredRow = -1
}
// 行分隔线
Rectangle {
width: parent.width
height: 1
color: borderColor
anchors.bottom: parent.bottom
}
// 表格内容
Text {
anchors.centerIn: parent
text: model.display
color: "#333"
}
}
}
}
Dialog {
id: addDialog
modal: true
anchors.centerIn: parent
standardButtons: Dialog.Ok | Dialog.Cancel
ColumnLayout {
anchors.fill: parent
Text { id: titleF; text: editMode? "修改" : "新增" }
TextField { id: nameF; Layout.fillWidth: true; placeholderText: "name" }
TextField { id: ageF; Layout.fillWidth: true; placeholderText: "age" }
}
function addData() {
editMode = false
addDialog.open()
}
function editData() {
if (selectedRow < 0) {
console.log("selectedRow < 0 !")
return
}
editMode = true
nameF.text = sqlModel.cell(selectedRow, 1)
ageF.text = sqlModel.cell(selectedRow, 2)
addDialog.open()
}
onAccepted: {
if (editMode) {
sqlModel.editRow(selectedRow, nameF.text, parseInt(ageF.text) )
} else {
sqlModel.appendRow(nameF.text, parseInt(ageF.text) )
}
}
}
}
代码说明
QML界面从上至下分为2部分,分别为 RowLayout
水平布局的操作栏 和 TableView
表格组件(显示sqlModel数据)。
操作栏(增、删、改、存)
- 新增:弹出Dialog对话框,填写name、age数据,点击"确定"后调用
appendRow
接口写入sqlModel
模型中。 - 删除:将当前记录行号传递给
sqlModel.removeRow
, 从模型缓存中删除,TableView
同步更新。 - 修改:将当前行号对应的name、age数据读取,填入
Dialog
对话框中,点击"确定"后调用sqlModel.editRow
修改模型缓存数据,TableView
同步更新。 - 保存:调用
sqlModel.saveAll()
一次性将之前的修改写进数据库中。
TableView(显示sqlModel数据)
在 TableView 中主要做了如下操作:
- 绑定模型(model属性) :将
sqlModel
模型中所有行的数据铺成格子,一行一条记录,三列对应数据库中的3个字段。 - 表头支持(HorizontalHeaderView) :默认的
TableView
表格组件不带表头,需要自行设置HorizontalHeaderView
的位置和绑定表格id(通过syncView
字段)。 - 样式定义(delegate部分) :每个格子都进行了样式定义,初始宽度大致为窗口的1/3,在结合
color
属性和MouseArea
鼠标事件下,进行了常态、鼠标滑过、鼠标选中等背景色的设置。 - 行分隔线 :将
Rectangle
组件高度设置为1,设置color
属性,布局在单元格下方。 - 显示表格内容 :将
Text
组件的text
属性与model.display
绑定,设置颜色和位置居中。
Dialog(增、改数据)
增加一行数据:addData
设置编辑模式变量 editMode
为 false
, 标题设置为"新增",弹出对话框用于新增数据。
编辑一行数据:editData
设置编辑模式变量 editMode
为 true
, 标题设置为"修改",弹出对话框,通过 sqlModel.cell
获取name、age数据,用于修改数据。
确认
点击"确认"按钮后,响应QML中 onAccepted
方法,将更改同步至 sqlModel
模型。
优化建议:
如果接口太多的情况下,可以单独建一个js文件,将function相关的代码迁移到js文件中,在qml使用 import
引入即可。
FAQ
1. 如何控制TableView某一列不显示?
在上述代码Main.qml中修改:
qml
TableView {
// 前段省略 ...
// 绑定模型
model: sqlModel
// 控制列宽, 设置第0列不可见
columnWidthProvider: function(col) {
return col === 0 ? 0 : width/2 - 5
}
// 自定义单元格, 后段省略 ...
delegate: Rectangle {
}
}
通过columnWidthProvider,设置列宽为0,即可控制表格某列不显示。效果如下:

设置第0列不显示,另外2列平均分配窗口列宽。
2. TableView Delegate中的row变量从何而来?
见Qt帮助手册,TableView中的Delegate字段描述:
The delegate provides a template defining each cell item instantiated by the view. The model index is exposed as an accessible index property. The same applies to row and column. Properties of the model are also available depending upon the type of Data Model.
委托提供了一个模板,用于定义视图实例化的每个单元格项。模型索引被公开为一个可访问的 index 属性。row 和 column 也是如此。模型中的属性也根据数据模型的类型而可用。
工程下载
GitCode下载链接: QML TableView:基于SQLite实现增删改查
