表格光好看还不够,很多场景下还得能直接改数据、能存到数据库。直接在单元格里编辑比弹个框改要顺手得多,而 SQLite 作为轻量级嵌入式数据库,跟 Qt 配合起来也很方便。
这篇文章用两个例子演示:第一个例子做一个单元格可编辑的表格,点一下价格或库存就能直接改,回车确认,只允许输入数字;第二个例子用 QSqlTableModel 对接 SQLite 数据库,实现数据的增删改查和持久化。
可编辑表格
一个商品价格表,点击价格或库存单元格进入编辑状态,弹出输入框,回车确认修改,有数字校验防止输错。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels
FadeInAnimation {
property int selectedRow: -1
property int hoveredRow: -1
readonly property int rowHeight: 40
readonly property color headerColor: "#1296FF"
readonly property color selectedColor: "#BCE6FF"
readonly property color hoverColor: "#E5F3FF"
readonly property color editingColor: "#FFE082"
readonly property color borderColor: "#e0e0e0"
property bool isEditing: false
property int editingRow: -1
property int editingColumn: -1
TableModel {
id: tableModel
TableModelColumn { display: "product" }
TableModelColumn { display: "price" }
TableModelColumn { display: "stock" }
rows: [
{ product: "笔记本电脑", price: "6999", stock: "15" },
{ product: "手机", price: "3999", stock: "25" },
{ product: "平板电脑", price: "4599", stock: "10" },
{ product: "智能手表", price: "1999", stock: "30" }
]
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
// 表头
Rectangle {
Layout.fillWidth: true
height: rowHeight
color: headerColor
radius: 4
Row {
anchors.fill: parent
Repeater {
model: ["产品", "价格 (可编辑)", "库存 (可编辑)"]
Rectangle {
width: parent.width / 3
height: parent.height
Text {
anchors.centerIn: parent
text: modelData
color: "white"
font.bold: true
font.pixelSize: 14
}
}
}
}
}
// 表格内容
TableView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: tableModel
columnSpacing: 1
rowSpacing: 1
delegate: Rectangle {
implicitWidth: 120
implicitHeight: rowHeight
color: (row === editingRow && column === editingColumn) ? editingColor :
row === selectedRow ? selectedColor :
row === hoveredRow ? hoverColor :
row % 2 ? "#f5f5f5" : "white"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
selectedRow = row
if (column > 0) {
editingRow = row
editingColumn = column
textInput.text = display
textInput.visible = true
textInput.focus = true
}
}
onEntered: hoveredRow = row
onExited: hoveredRow = -1
}
Rectangle {
width: parent.width
height: 1
color: borderColor
anchors.bottom: parent.bottom
}
Text {
anchors.centerIn: parent
text: column === 1 ? "¥" + display : display
color: column === 1 ? "#E91E63" : "#333333"
font.pixelSize: column === 1 ? 14 : 13
font.bold: column === 1
visible: !(row === editingRow && column === editingColumn)
}
TextField {
id: textInput
anchors.fill: parent
anchors.margins: 2
visible: row === editingRow && column === editingColumn
text: display
selectByMouse: true
horizontalAlignment: TextInput.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
onAccepted: {
if (/^\d+$/.test(text)) {
let newCell = tableModel.getRow(row)
newCell[column === 1 ? "price" : "stock"] = text
tableModel.setRow(row, newCell)
}
visible = false
editingRow = -1
editingColumn = -1
}
onFocusChanged: {
if (!focus) {
visible = false
editingRow = -1
editingColumn = -1
}
}
validator: IntValidator { bottom: 0; top: 999999 }
}
}
columnWidthProvider: function(col) { return (width - 2) / 3 }
rowHeightProvider: function(row) { return rowHeight }
}
Text {
Layout.fillWidth: true
text: "点击价格或库存单元格进行编辑 · 仅允许整数 · 回车确认"
color: "#666"
font.pixelSize: 11
horizontalAlignment: Text.AlignHCenter
}
}
}
编辑的核心思路是「同一个单元格里放两个东西,编辑时显示输入框,否则显示文字」。delegate 里同时有一个 Text 和一个 TextField,通过 visible 属性控制谁显示谁隐藏。editingRow 和 editingColumn 两个变量记录当前正在编辑的单元格位置。
点击单元格时,判断是不是产品列(column > 0),是的话就进入编辑状态:把当前值填进输入框、让输入框显示、获取焦点。产品列不让改,所以点了只选中不编辑。
确认修改有两种方式:按回车或者输入框失去焦点。回车时走 onAccepted,先校验输入是不是纯数字,校验通过就把新值写回模型。这里用的是 getRow() 配合 setRow() 两个方法:先用 getRow(row) 把当前行整份取出来,改掉目标单元格,再 setRow(row, newCell) 写回。注意 rows 属性只能读、不能直接整体赋值回去,想要改数据务必走 setRow(),否则会把单元格包成 QVariantList 导致类型校验失败。
输入框加了 IntValidator 限制只能输整数,从输入层面就拦住非法值。
SQLite 表格:数据持久化
一个人员信息表,数据存在 SQLite 数据库里,支持新增、删除、修改,关闭程序再打开数据还在。表格上方有操作按钮,选中一行后可以删除或修改。

篇幅太长,代码就不贴出来了,详见文章底部的Github链接。
这个例子用 C++ 的 QSqlTableModel 来对接 SQLite,QML 这边只负责展示和调用。QSqlTableModel 是 Qt 提供的 SQL 表格模型,封装了常见的增删改查操作,不用自己写 SQL 语句就能用。
核心流程是这样的:程序启动时打开或创建数据库和 people 表,实例化 SqlTableModel 并注册到 QML 上下文,TableView 的 model 直接指向这个 C++ 模型。新增、删除、修改都通过模型的 Q_INVOKABLE 方法来操作,操作完调 saveAll() 提交到数据库,再调 select() 刷新视图。
模型里暴露了一个 count 属性,用来在 QML 显示「共 N 条记录」------因为 rowCount 在 QML 里是方法不是属性,直接绑定会拿到函数体,而且增删后不会自动刷新,包一层带 NOTIFY 的属性就解决了。
表头用了 HorizontalHeaderView,跟 TableView 通过 syncView 关联起来,列宽自动同步,还支持拖拽调整列宽。第 0 列是数据库的主键 id,宽度设为 0 隐藏掉,用户看不到但数据还在,删除修改时用得上。
适用场景:本地数据存储、需要持久化的小型管理工具、离线应用的数据表格。
已验证环境:
- Qt 版本:Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_tableview