QML TableView:基于SQLite实现增删改查

目录

前言

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

设置编辑模式变量 editModefalse, 标题设置为"新增",弹出对话框用于新增数据。

编辑一行数据:editData

设置编辑模式变量 editModetrue, 标题设置为"修改",弹出对话框,通过 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实现增删改查

相关推荐
JuneXcy3 小时前
第2章 数据库系统的核心--数据模型
数据库·mysql·oracle
Ahern_4 小时前
崖山数据库安装部署
linux·数据库
斯普信专业组4 小时前
Redis集群平滑扩缩容与槽位迁移实战指南
数据库·redis·槽位迁移
米诺zuo4 小时前
datagrip配置新的数据库
数据库
火星MARK4 小时前
RAID详解
数据库·oracle
JAVA学习通4 小时前
Spring AI与DeepSeek实战:打造企业级智能体
数据库
安审若无5 小时前
Oracle 打补丁指南
数据库·oracle
樱花的浪漫5 小时前
Cuda reduce算子实现与优化
数据库·人工智能·深度学习·神经网络·机器学习·自然语言处理
啊森要自信5 小时前
【MySQL 数据库】MySQL用户管理
android·c语言·开发语言·数据库·mysql