如何在QML中按层级展示数据并实现多选与折叠功能?
实现Qt树形数据展示(带复选框与展开折叠)
针对你的需求,最优方案是后端将扁平SQL数据转换为树形结构,前端用QtQuick.Controls的TreeView组件实现展开折叠与复选框多选,具体实现如下:
1. 后端数据预处理(Python)
无需在SQL中添加深度字段,直接将查询到的扁平数据转换为包含子节点、展开状态、复选状态的树形结构,再封装为QStandardItemModel传给QML:
from PySide6.QtGui import QStandardItemModel, QStandardItem from PySide6.QtCore import Qt def build_tree_from_flat_data(flat_data): """将扁平SQL数据转换为树形结构""" item_map = {} root_nodes = [] for item in flat_data: node = { "idData": item["idData"], "DataName": item["DataName"], "DataParentId": item["DataParentId"], "children": [], "expanded": True, # 默认展开子节点 "checked": False # 默认未选中 } item_map[item["idData"]] = node if item["DataParentId"] is None: root_nodes.append(node) else: parent_node = item_map.get(item["DataParentId"]) if parent_node: parent_node["children"].append(node) return root_nodes def build_standard_item_model(tree_nodes): """构建QStandardItemModel,供QML TreeView使用""" model = QStandardItemModel() def add_node(parent_item, node): item = QStandardItem(node["DataName"]) # 绑定自定义数据角色 item.setData(node["idData"], Qt.UserRole) item.setData(node["expanded"], Qt.UserRole + 1) item.setData(node["checked"], Qt.UserRole + 2) # 启用复选框 item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked if node["checked"] else Qt.Unchecked) parent_item.appendRow(item) # 递归添加子节点 for child in node["children"]: add_node(item, child) add_node(model.invisibleRootItem(), tree_nodes) return model # 示例:从SQL获取数据后处理 # flat_data = 你的SQL查询结果(列表字典格式) # tree_model = build_standard_item_model(build_tree_from_flat_data(flat_data)) # 将tree_model传给QML上下文
2. QML前端实现
使用TreeView组件,自定义委托添加复选框,并实现展开折叠、复选框联动逻辑:
import QtQuick 2.15 import QtQuick.Controls 2.15 TreeView { id: dataTree anchors.fill: parent model: treeModel // 对应Python传入的QStandardItemModel // 定义列 TableViewColumn { title: "数据列表" role: "display" width: parent.width delegate: Row { spacing: 8 anchors.verticalCenter: parent.verticalCenter // 多选复选框 CheckBox { id: checkBox checkState: model.checkState tristate: true // 支持半选状态(子项部分选中时) onCheckStateChanged: { // 更新自身选中状态 model.setData(model.index, checkState, Qt.CheckStateRole) // 递归更新子项状态 updateChildrenCheckState(model.index, checkState) // 更新父项状态 updateParentCheckState(model.index.parent()) } } // 数据名称 Text { text: model.display verticalAlignment: Text.AlignVCenter } } } // 展开/折叠节点时更新model中的expanded状态 onExpanded: model.setData(index, true, Qt.UserRole + 1) onCollapsed: model.setData(index, false, Qt.UserRole + 1) // 递归更新子项复选状态 function updateChildrenCheckState(parentIndex, state) { const rowCount = model.rowCount(parentIndex) for (let i = 0; i < rowCount; i++) { const childIndex = model.index(i, 0, parentIndex) model.setData(childIndex, state, Qt.CheckStateRole) updateChildrenCheckState(childIndex, state) } } // 更新父项复选状态(处理半选逻辑) function updateParentCheckState(parentIndex) { if (!parentIndex.isValid()) return let allChecked = true let anyChecked = false const rowCount = model.rowCount(parentIndex) for (let i = 0; i < rowCount; i++) { const childIndex = model.index(i, 0, parentIndex) const childState = model.data(childIndex, Qt.CheckStateRole) if (childState !== Qt.Checked) allChecked = false if (childState !== Qt.Unchecked) anyChecked = true } let parentState = Qt.Unchecked if (allChecked) { parentState = Qt.Checked } else if (anyChecked) { parentState = Qt.PartiallyChecked } model.setData(parentIndex, parentState, Qt.CheckStateRole) // 递归更新上级父项 updateParentCheckState(parentIndex.parent()) } }
3. 核心优势
- 性能更优:后端预处理树形结构,避免QML端频繁遍历过滤数据
- 原生支持:TreeView自带展开折叠逻辑,无需手动实现嵌套ListView
- 完整联动:复选框支持父子联动(全选/半选/取消),符合用户操作习惯
内容的提问来源于stack exchange,提问作者kixrogu
相关产品推荐
相关产品推荐

