QML TreeView 简单使用

news2024/12/24 2:35:00

本文主要介绍 QML 中 TreeView 的基本使用方法,包括:TreeView的适用场景;

控件简介

QML TreeView 是 Qt Quick 中的一个组件,用于显示树形结构的数据。它提供了一种以层次结构方式展示数据的方式,其中每个节点可以包含子节点。

以下是 QML TreeView 的一些关键概念和特点:

  1. Model-View 架构:QML TreeView 遵循 Model-View 架构,其中数据模型(通常是 QAbstractItemModel 的子类)负责提供数据,而视图组件则负责显示和交互。数据模型提供了节点层次结构以及每个节点的数据。
  2. Delegate 委托:TreeView 使用委托来定义每个节点的外观和行为。委托可以自定义,允许您根据需求定制每个节点的呈现方式。
  3. 展开和折叠:TreeView 支持展开和折叠节点的功能。当节点具有子节点时,可以通过点击节点旁边的展开/折叠按钮或者通过编程方式来切换节点的展开状态。
  4. 自定义节点样式:您可以自定义节点的样式,包括文本颜色、背景色、字体、图标等。通过使用不同的委托和属性设置,您可以实现丰富多样的外观效果。
  5. 选择和焦点:TreeView 支持节点的选择和焦点管理。您可以根据需要选择一个或多个节点,并可以通过编程方式操作当前焦点节点。
  6. 事件处理:TreeView 允许处理节点上的各种事件,例如点击、双击、拖放等。您可以根据需要响应这些事件并执行相应的操作。

QML TreeView 在许多应用中都有广泛的应用场景,特别是需要展示层次结构数据的场景。例如,文件浏览器、目录结构、组织架构图等都可以使用 TreeView 来呈现数据并提供交互性。

在这里插入图片描述

QML TreeView 提供了灵活的自定义选项,可以根据需求定制节点的样式和交互行为。通过自定义委托元素,可以为每个节点定义不同的显示方式,以满足特定的设计要求。

前置准备

阅读本节内容前请确保您了解 Qt 中与模型/视图架构相关的内容、与 Python 中调用 QML 相关的内容,包括:

  1. 模型(model)、视图(View)和委托(Delegate)之间的关系。
  2. QML 调用 Python 模型。

如不是特别熟悉,请先阅读《QML 与 Python 交互》,若您想更深入了解,请参阅官方文档《Python-QML integration》、《》。

环境准备:

  • OS:Windows 10 x64
  • Python环境:Python 3.8.x 及以上
  • Python包:PySide6(version 6.3.x 及以上)

使用方法

通过查阅官方文档,我们发现,官方提供了 QML 的 TreeView 的示例代码,但是还需要我们自己实现其对应的 model 部分,下边我们使用 Qt 官方提供的示例代码,给它匹配一个 model ,完成一个完整的节点树,主要我们可以概括为两部分:

  1. 在 Python 文件中定义 TreeView 对应的 model。
  2. 将 model 暴露给 QML ,绑定到 TreeView。

定义 model

在 QML 中,TreeView 使用的 model 必须是继承自 QAbstractItemModel 类的子类,在对 QAbstractItemModel 进行子类实例化时,至少必须实现 index() , parent() , rowCount() , columnCount()data() 。简而言之,就是:在 Python 文件中定义一个类,这个类继承自 QAbstractItemModel ,并且在这个子类中要重写这几个方法。

可选需重写的虚函数:

  • flag——是为了实现可编辑才重写的。
  • setData——是为了编辑之后把数据保存到数据结构才重写的。

要在模型中启用编辑,还必须实现:

  • setData()
  • flags()(要确保返回 ItemIsEditable)

控制模型标题的显示方式,可以实现:

  • headerData()
  • setHeaderData()

在重新实现 setData()setHeaderData() 函数时,必须分别显式地发出 dataChanged()headerDataChanged() 信号。

绑定 model 给 TreeView

在 main.py 中添加这行代码,表示将 treeModel 暴露给 QML 上下文环境中,在 QML 中可以当组件直接调用:

# ...
engine.rootContext().setContextProperty('treeModel', treeModel)
# ...

在 QML 中可以直接使用,如:

// ...
model: treeModel
// ...

这样就完成了 Python 数据模型和 QML 文件的绑定。

完整示例

项目目录结构:

Project
  ├─ main.qml
  ├─ man.py
  └─ TreeModel.py

完整代码:

// main.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

 Window {
     width: 600
     height: 400
     visible: true

     TreeView {
         anchors.fill: parent
         // The model needs to be a QAbstractItemModel
         model: treeModel

         delegate: Item {
             id: treeDelegate

             implicitWidth: padding + label.x + label.implicitWidth + padding
             implicitHeight: label.implicitHeight * 1.5

             readonly property real indent: 20
             readonly property real padding: 5

             // Assigned to by TreeView:
             required property TreeView treeView
             required property bool isTreeNode
             required property bool expanded
             required property int hasChildren
             required property int depth

             TapHandler {
                 onTapped: treeView.toggleExpanded(row)
             }

             Text {
                 id: indicator
                 visible: treeDelegate.isTreeNode && treeDelegate.hasChildren
                 x: padding + (treeDelegate.depth * treeDelegate.indent)
                 anchors.verticalCenter: label.verticalCenter
                 text: "▶"
                 rotation: treeDelegate.expanded ? 90 : 0
             }

             Text {
                 id: label
                 x: padding + (treeDelegate.isTreeNode ? (treeDelegate.depth + 1) * treeDelegate.indent : 0)
                 width: treeDelegate.width - treeDelegate.padding - x
                 clip: true
                 text: model.display
             }
         }
     }
 }

这是一个使用 QML 语言编写的树形视图(TreeView)的界面定义。让我们逐行解释:

  1. TreeView: 树形视图组件,用于展示树状结构的数据。
  2. anchors.fill: parent: 将 TreeView ​组件的四个边界锚定到父级窗口,使其充满整个窗口空间。
  3. model: treeModel: 指定 TreeView ​使用的数据模型。在这里,treeModel是一个 QAbstractItemModel ​类型的模型,它提供了树形结构的数据。
  4. delegate: 定义了用于绘制每个树节点的委托元素。
  5. implicitWidthimplicitHeight: 定义委托元素的隐式宽度和高度。这些属性决定了每个委托元素的大小。
  6. indentpadding: 定义了缩进和填充的大小,用于控制节点在水平方向上的位置。
  7. TapHandler: 定义了一个用于处理点击事件的处理器。当用户点击委托元素时,将触发onTapped事件。
  8. indicator: 一个用于显示展开/折叠状态的文本元素。根据节点是否是父节点(isTreeNode)和是否有子节点(hasChildren)来决定是否显示。
  9. label: 显示节点的文本内容。根据节点的深度(depth)和是否是父节点来确定水平偏移量。

创建一个带有树形结构的 TreeView,并为每个节点定义了自定义的委托元素。委托元素根据节点的深度和展开状态来显示节点的文本和展开/折叠指示符。你需要提供一个名为treeModel的 QAbstractItemModel 类型的模型来作为 TreeView 的数据源。

# TreeModel.py
from PySide6.QtCore import QAbstractItemModel, QModelIndex, Qt


# 自定义的树形节点类
class TreeNode:
    # 初始化节点数据和父亲节点
    def __init__(self, data, parent=None):
        # 存储节点数据
        self._data = data
        # 存储父节点
        self._parent = parent
        # 存储子节点
        self._children = []

    # 添加一个子节点
    def appendChild(self, child):
        self._children.append(child)

    # 删除一个子节点
    def removeChild(self, item):
        self._children.remove(item)

    # 获取某一行的子节点
    def child(self, row):
        return self._children[row]

    # 获取子节点的个数
    def childCount(self):
        return len(self._children)

    # 获取列数
    def columnCount(self):
        return 1

    # 获取节点某一列的数据
    def data(self, column):
        if column == 0:
            return self._data

    # 获取父节点
    def parent(self):
        return self._parent

    # 获取该节点在父节点中的行号
    def row(self):
        if self._parent:
            return self._parent._children.index(self)

    # 判断节点是否包含子节点
    def hasChildren(self):
        return len(self._children) > 0


# 自定义的树形数据模型
class TreeModel(QAbstractItemModel):
    # 初始化根节点
    def __init__(self, root, parent=None):
        super().__init__(parent)
        # 如果没有传入根节点,则创建一个空的根节点作为根
        self._root = root or TreeNode(None)
        # self._root = root

    # 获取某个节点在模型中的Index
    def index(self, row, column, parent=QModelIndex()):
        if not self.hasIndex(row, column, parent):
            return QModelIndex()

        if not parent.isValid():
            parentItem = self._root
        else:
            parentItem = parent.internalPointer()

        childItem = parentItem.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QModelIndex()

    # 获取某个节点的父节点的Index
    def parent(self, index):
        if not index.isValid():
            return QModelIndex()

        childItem = index.internalPointer()
        parentItem = childItem.parent()

        if parentItem == self._root:
            return QModelIndex()

        return self.createIndex(parentItem.row(), 0, parentItem)

    # 获取某个节点的子节点个数
    def rowCount(self, parent=QModelIndex()):
        if parent.column() > 0:
            return 0

        if not parent.isValid():
            parentItem = self._root
        else:
            parentItem = parent.internalPointer()

        return parentItem.childCount()

    # 获取列数,这里只有一列
    def columnCount(self, parent=QModelIndex()):
        return 1

    # 获取某个节点的数据
    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None

        item = index.internalPointer()
        if role == Qt.DisplayRole:
            return item.data(index.column())

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags

        return super().flags(index)

这段代码是一个用于创建树形数据模型的类 TreeModel,它是基于 QAbstractItemModel 的子类,用于在 PySide6 中实现树形视图的数据模型。

代码中定义了一个自定义的树形节点类 TreeNode,每个节点包含一个数据项和对父节点和子节点的引用。TreeNode 类提供了一些方法来操作节点的子节点,如添加子节点、删除子节点等。还提供了一些方法用于获取节点的数据、父节点、子节点数量等。

TreeModel 类的构造函数接受一个根节点作为参数,并将其存储在成员变量 _root 中。如果没有传入根节点,则创建一个空的根节点。该类还实现了一些必要的方法,使得树形数据模型能够与视图进行交互。

以下是对 TreeModel 类中重要方法的解读:

  • index(self, row, column, parent=QModelIndex()): 返回指定行和列的节点在模型中的索引。如果索引无效,则返回 QModelIndex()。如果父索引无效,则返回根节点;否则,返回父节点的子节点。
  • parent(self, index): 返回给定索引的父节点的索引。如果索引无效,则返回 QModelIndex()。如果父节点是根节点,则返回无效索引;否则,返回父节点的索引。
  • rowCount(self, parent=QModelIndex()): 返回给定父索引下的子节点数量。如果父索引的列数大于 0,则返回 0。如果父索引无效,则返回根节点的子节点数量;否则,返回父节点的子节点数量。
  • columnCount(self, parent=QModelIndex()): 返回模型中的列数,这里固定为 1。
  • data(self, index, role=Qt.DisplayRole): 返回给定索引处节点的数据。如果索引无效,则返回 None。如果角色是 Qt.DisplayRole,则返回节点的数据。
  • flags(self, index): 返回给定索引的标志。如果索引无效,则返回 Qt.NoItemFlags,表示没有任何标志。
# main.py
from PySide6.QtCore import QUrl
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from TreeModel import TreeModel,TreeNode
import sys


if __name__ == '__main__':

    # 创建树形结构
    rootItem = TreeNode("root")
    childItem1 = TreeNode("child1", rootItem)
    childItem2 = TreeNode("child2", rootItem)
    childItem3 = TreeNode("child3", rootItem)
    subChildItem1 = TreeNode("sub_child1", childItem1)
    subChildItem2 = TreeNode("sub_child2", childItem1)

    rootItem.appendChild(childItem1)
    rootItem.appendChild(childItem2)
    rootItem.appendChild(childItem3)
    childItem1.appendChild(subChildItem1)
    childItem1.appendChild(subChildItem2)

    # 创建树形数据模型
    treeModel = TreeModel(rootItem)

    # 启动
    app = QGuiApplication([])
    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty('treeModel', treeModel)
    engine.load(QUrl.fromLocalFile('main.qml'))
    engine.quit.connect(app.quit)
    sys.exit(app.exec())

这是一个使用 PySide6 构建的应用程序的入口点 main.py。它创建了一个树形结构,并将其与自定义的树形数据模型 TreeModel 结合使用,最后通过 Qt Quick(QML)界面进行展示。

  • 首先,代码导入了必要的 PySide6 模块和类,以及自定义的 TreeModelTreeNode 类。
  • if __name__ == '__main__': 块中,首先创建了树形结构。根节点是一个名为 “root” 的节点,然后创建了几个子节点,并将它们作为根节点的子节点。子节点也可以有它们自己的子节点。
  • 接下来,通过将根节点传递给 TreeModel 类的构造函数,创建了一个树形数据模型 treeModel
  • 之后,创建了 QGuiApplication 实例 app,用于启动应用程序。
  • 创建了 QQmlApplicationEngine 实例 engine,用于加载和运行 QML 界面。
  • 通过 engine.rootContext().setContextProperty('treeModel', treeModel)treeModel 设置为 QML 上下文属性,以便在 QML 中可以访问该树形数据模型。
  • 使用 engine.load(QUrl.fromLocalFile('main.qml')) 加载 QML 文件,该文件定义了应用程序的界面。
  • 最后,通过 engine.quit.connect(app.quit)enginequit 信号连接到 appquit 槽,以便在关闭应用程序时能够正确退出。
  • 最后使用 sys.exit(app.exec()) 启动应用程序的事件循环。

运行命令:

python main.py

未实现

  • 双击编辑节点

参考

[1].TreeView QML Type.[EB/OL].[2023-6-15].https://doc.qt.io/qt-6/qml-qtquick-treeview.html

[2].QAbstractItemModel.[EB/OL].[2023-6-15].https://doc.qt.io/qtforpython-6/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.PySide6.QtCore.QAbstractItemModel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/652930.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蒸汽朋克 VoxEdit 竞赛

欢迎来到蒸汽朋克 VoxEdit 比赛,在这个充满装备和蒸汽的世界里,创意和体素互相碰撞,激发无尽创意! 蒸汽朋克是将过去重新想象为带有未来主义色彩的风格。通过将维多利亚时代的优雅与蒸汽动力机械和黄铜装置相结合,将您…

【强烈推荐】 十多款2023年必备国内外王炸级AI工具 (免费 精品 好用) 让你秒变神一样的装逼佬感受10倍生产力 (2) AI修音

🚀 个人主页 极客小俊 ✍🏻 作者简介:web开发者、设计师、技术分享博主 🐋 希望大家多多支持一下, 我们一起进步!😄 🏅 如果文章对你有帮助的话,欢迎评论 💬点赞&#x1…

C# 特性

目录 什么是特性 Serializable DllImport Obsolete Conditional 自定义Attribute 特性定义:特性其实是一个类,是直接/间接继承自Attribute,约定俗成是以Attribut结尾,在标记的时候以[ ]包裹,尾部的Attribut可以…

竞赛知识点5【图论】

文章目录 前言基本概念图的定义和分类路径顶点的度、入度、出度图的存储图的DFS遍历图的BFS遍历判断是否为欧拉图拓扑排序关键路径前言 图论起源于著名的哥尼斯堡七桥问题——从这四块陆地中任何一块开始,通过每一座桥正好 一次,再回到起点。欧拉在 1736 年解决了这个问题,…

【计算机视觉】千字汇总:一文读懂计算机视觉,干货满满记得收藏

文章目录 一、前言二、计算机视觉为什么重要三、什么是计算机视觉四、计算机视觉的基本原理五、计算机视觉的典型任务5.1 图像分类5.2 目标检测5.3 语义分割5.4 实例分割5.5 目标追踪 六、计算机视觉在日常生活中的应用场景6.1 门禁、支付宝上的人脸识别6.2 停车场、收费站的车…

Android——布局管理器(十一)

1. 线性布局管理器:LinearLayout 1.1 知识点 (1)布局管理器简介; (2)线型布局管理器的配置; (3)通过Activity程序进行线型布局 1.2 具体内容 在android中&#xff…

java elasticsearch 实现以图搜图效果

前言: 现在需要用javaelasticsearch的方式实现以图搜图的效果,根据下面的文章内容做了一点修改 相关文章:https://blog.csdn.net/m0_52640724/article/details/129357847 一、相关环境 java:jdk11 elasticsearch:7.17…

行情不好进阶困难?那是因为你没有选对方向

关注“软件测试藏经阁”微信公众号,回复暗号【软件测试】,即可获取氪肝整理的全套测试资源 IT行情彻底崩盘了? 相信凡是在抖音关注过互联网相关内容的同学,应该会经常听到这句话吧!没错就是号称“某蛙之父”的那个人天…

【LeetCode】HOT 100(13)

题单介绍: 精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

JavaScript中getElementById与querySelector区别

JavaScript中getElementById与querySelector区别 1、getElement(s)Byxxxx 的用法1.1 getElementById() 方法1.2 getElementsByClassName() 方法1.3 getElementsByTagName() 方法 2、querySelector() 和 querySelectorAll() 用法2.1 querySelector() 方法2.2 querySelectorAll()…

Big O示例与Python数据结构的Big O

在Big-O示例部分的第一部分,我们将介绍各种Big-O函数的各种迭代。 让我们从一些简单的例子开始,探索大O是什么。 O(1) Constant def func_constant(values):Prints first item in a list of values.print(values[0])func_constant([1,2,3])# 1请注意&a…

避雷器计数器测试仪

原理 图1所示为JS动作记数器的原理接线图。图1(a)为JS动作记数器的基本结构,即所谓的双阀片式结构。当避雷器动作时,放电电流流过阀片R1,在R1上的压降经阀片R2给电容器C充电,然后C再对电磁式记数器的电感线…

easyX基本概念(注释版)

0.前言 本次我给您带来easyX库系列的博文,本系列博文目的在于对原easyX库文档进行一个补充和注解,重在补充测试样例和实践。 easyX库本身并不值得过于学习,但是作为有C语言基础的C爱好者,学习easyX能让您对IT技术更加感兴趣。用…

AI完成音频创作,击败99%作者

使用AI完成音频创作 ,击败99%同类创作者 ,享受持续广告变现收益 ,下面我们来看下如何使用把~ 音频生成和投放可以分为以下两步骤: 使用AI效能公众号完成内容创作;利用喜马拉雅云剪辑发布内容 1. 内容生成 打开AI效能公…

Altium Designer 15 (AD15)新建元件库

1. 连接线 1.1 设置连接线的默认颜色,宽度:点击图标 --》按Tab键 1.2 默认使用蓝色,通用 2. 调出来元件库,然后按照元件的英文名字搜元件,拖到左边画图区域就可以了 3. 自己画一个元器件,自定义元器件 3.1…

6.14 消息队列

目录 消息队列 消息队列结构 消息队列使用步骤 消息队列创建/打开-msgget 消息队列创建/打开 - 示例 消息发送 – msgsnd 消息格式 消息发送 - 示例 笔记 消息队列 消息队列是System V IPC对象的一种 消息队列由消息队列ID来唯一标识 消息队列就是一个消息的列表。…

解决Idea中日志文件log4j.xml中http//jakarta.apache.org/log4j爆红,报错此 uri is not registered

在Idea中&#xff0c;配置log4j.xml出现“http //jakarta.apache.org/log4j/ uri is not registered”的错误信息&#xff0c;解决步骤如下&#xff1a; 1、原始的log4j.xml配置文件&#xff1a; <?xml version"1.0" encoding"GB2312" ?> <!…

ESP32(Micro Python)LVGL 四路ADC

本程序布局与上一个程序相同&#xff0c;引脚不重合&#xff0c;可以在不更换外设的情况下切换程序。由于仪表盘显示的数值范围不可调&#xff0c;实际显示的值为测量值占量程的百分比。 代码如下 import lvgl as lv import time from espidf import VSPI_HOST from ili9XX…

chatgpt赋能python:Python如何在输入之前等待30秒

Python如何在输入之前等待30秒 作为一名编程工程师&#xff0c;程序的性能和用户体验都是非常重要的。在用户输入数据之前等待一段时间可以帮助我们避免不必要的错误和提高程序的稳定性。本文将介绍如何使用Python等待30秒在输入数据。 使用Python的time模块 Python的time模…

【强烈推荐】 十多款2023年必备国内外王炸级AI工具 (免费 精品 好用) 让你秒变神一样的装逼佬感受10倍生产力 (4) AI办公

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…