You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 16:40:43