QT树形组件创建,增加,删除入门学习----绝对细致,一篇入门

QT树形组件创建,增加,删除入门学习----绝对细致,一篇入门



Author:qyan.li

Date:2022.3.22

Topic:借助于python语言在QT中创建树形组件

一、前言:

QT创建图形化界面时,经常会遇到需要创建树形组件的需求,比如在左侧创建"文件目 录"栏,这都会应用到树形组件。
QT for python中常用的树形组件有两种,分别为TreeWidgetTreeView,这两种类都可以创建树形组件,但是特点不尽相同。

二、TreeWidget组件

TreeWidget类是QT for python创建树形结构的常用组件,在本部分主要针对于TreeWidget构建树,增加、删除子节点的相关操作。

  • 树构建基本配置:

    1. 窗口设置:

       # 设置窗口标题,大小
       self.setWindowTitle('TreeWidget应用示例')
       self.resize(400,300)
      
    2. 树基本设置:

      # 实例化QTreeWidget对象,并初始化基本信息(标签数目,标签名称)
      self.tree = QTreeWidget()
      self.tree.setColumnCount(2) # 设置标签个数
      self.tree.setHeaderLabels(['key','value']) # 设置标签内容
      
  • 树(TreeWidget对象增加结点)

    ​ 在讲述增加节点的内容之前,区分两个概念:TreeWidgetTreeWidgetItem,二者是可以在TreeWidget中实例化的两个对象,一般情况下分别指代本Tree和该Tree下的子节点

    • 借助于构造方法增加结点:

      root1 = QTreeWidgetItem(self.tree) # 内部传入TreeWidget对象
      

      Tips:传入的参数即为生成对象的父节点

    • 借助于AddTopLevelItem()方法:

      root2 = QTreeWidgetItem() 
      self.tree.addTopLevelItem(root2) # 在self.tree下添加子节点root2
      

      Tips:调用方式为TreeWidget.addTopLevelItem(TreeWidgetItem)

  • 结点(TreeWidgetItem对象增加结点)

    • 借助于构造方法:

      TreeWidget方法等同,参数传入TreeWidgetItem对象

    • 借助于addChild方法:

      child12 = QTreeWidgetItem()
      root1.addChild(child12) # 在root1总添加子节点child12
      
    • 借助于insertChild方法:

      child13 = QTreeWidgetItem()
      root1.insertChild(0,child13)
      

      Tips:

      • insertChildaddChild使用方法类似,insertChildindex参数,可以指定在特定位置插入
      • 针对于同一个父节点的所有子节点,index从零开始递增
  • 设置节点信息

    ​ 在"树的基本设置环节",提及树可以设置若干个标签用于指示树的信息,针对于节点,可以借助于setText()函数实现结点信息的设置

    child13.setText(0,'Temperature') # index指代标签1或者2
    child13.setText(1,'27')
    

写在这里的小总结:

为什么在这里写总结,因为结点的增加函数设置体现QT for python函数设置的思想:

  1. TreeWidget和TreeWidget概念的区别与分离
  2. 无论是增加还是删除函数,均设置多实现函数供用户选择(往往是默认和自定义<insertChildaddChild>),这点和数据结构的构建思想很像,这点会在后续删除和TreeView操作中非常常见
  • 结点(TreeWidgetItem对象删除结点)

    1. removeChild()函数

      root2.removeChild(child21) # 内部传入结点对象,返回值为None
      
    2. takeChild()函数

      root1.takeChild(2) # 传入待删除对象的index,返回值为被删除的对象本身
      

      小Tips:

      takeChildren()函数,内部不需要传入任何参数,删除此item下的所有子节点

    小总结:

    removeChild()takeChild()函数均可以完成删除结点操作,区别在于返回值return不同传入参数不同

先挖一个小坑,后续再填((●’◡’●))


2022.3.24 15:39 来填坑啦!!但是好像又要留下另外一个坑

三、QTreeView组件

QTreeViewQT for python创建树形结构时常用的另一个组件,QTreeWidgetQTreeView更简单,但是灵活性没有QTreeView高,QTreeView应用StandardItemModel模型,下面针对于该组件依旧阐述增加,删除,修改,选中四个方面的操作

  • 树结构基本配置

    1. 初始化窗口基本信息

      # 设置窗口标题,大小
      self.setWindowTitle('StandardItemModel')
      self.resize(520,300)
      
    2. 初始化树结构基本信息

      # 设置表头相关信息
      model = QStandardItemModel(self) # 实例化对象
      model.setHorizontalHeaderLabels(['key','value'])
      
  • QTreeView增加条目

    1. 借助于appendRow()方法

      item1 = QStandardItem('University') # 创建QStandardItem对象
      model.appendRow(item1) # 将Item对象添加至Model对象中
      

      Tips:

      • appendRow方法不仅仅可以应用于Model对象添加条目,Item对象添加条目同样可以使用
      • QStandardItemModelQStandardItem对象的区别与QTreeWidget中相同
    2. 借助于insertRow()方法

      model.insertRow(1,QStandardItem('Python'))
      model.insertRow(0,QStandardItem('code'))
      

      Tips:

      • QtreeWidget相同,insertRowappendRow方法的区别在于是否可以自定义指定位置
      • insertRow方法不仅仅适用于QStandardItenModel对象,同样是适用于QStandardItem对象
    3. setItemsetChild方法

      ​ 如果仔细观察,就会发现上述两种添加结点的方法只能在第一列添加内容(也就是本文测试中的column = 'key'列),但是后续column的内容无法借助于上述函数添加,必须借助于接下来介绍的set方法实现

      # 针对于QstandardItemModel对象的setItem方法
      model.setItem(1,0,QStandardItem('python'))
      model.setItem(1,1,QStandardItem('python1'))
      # 针对于QStandardItem的setChild方法
      child3.setChild(0,1,QStandardItem('Test'))
      child3.setChild(1,1,QStandardItem('Test1'))
      

      Tips:

      • QStandardItemModel对象调用setItem方法,QStandardItem对象调用setChild方法
      • QStandardItemModelQTreeWidget不同的一点在于,在QStandardItemModel中,无论哪一个cloumn下的词条内容均为Item对象,string类型的字符串是无法进行设置的
      • 个人发现,词条在进行添加时,同一个Item对象仅能使用一次,意味着每个词条仅能添加一次,后续使用会失效
  • QTreeView删除结点

    QTreeView中无论QStandardItemModel还是QStandardItem的删除操作,都可以分为大致的几个类别进行讲解

    1. 按照返回值类型return type

      删除操作按照返回值类型主要分为takeremove两种类型的函数

      简单理解:

      remove仅进行删除操作,而take函数会将被删去的结点返回回来

    2. 按照删除对象

      删除操作中按照删除对象可以大致分为三类:删除Row(整行),删除Cloumn(整列),删除特定结点(item或者child)

      ​ 因此,如果我们将上述分类模式组合,就可以得到下面若干函数:

      • removeRow,RemoveColumn
      • takeRow,takeColumn,takeChild,takeItem
      # 针对于QStandardItemModel的删除操作
      model.removeRow(0) # 传入index标签
      model.removeColumn(1)
      model.takeRow(1)
      model.takeColumn(1)
      model.takeItem(1,1) # 传入坐标
      
      # 针对于QStandardItem的删除操作
      child3.removeRow(1) # 传入index标签
      child3.removeColumn(1)
      child3.takeChild(1,1) # 传入坐标
      child3.takeColumn(1) 
      child3.takeRow(1)
      

      Tips:

      • 与QTreeWidget类似,Item方法适用于ModelChild方法适用于Item
  • QTreeView常用函数

    1. child函数

      child函数可以用于获得某个结点下特定位置的子节点

      item = child3.child(1,1)
      
      • 可以猜测,对应于QStandardItemModel模块存在item函数
    2. text函数

      text函数可以用于获得某个结点的内容,其中不需要传入任何参数

      text = child3.text()  
      
    3. modelparent函数

      modelparent函数用于获得某个结点,其所对应的model和父节点

  • 激发事件设置

    激发事件的设置需要借助于QTreeView

    treeView.selectionModel().currentChanged.connect(self.OperationTest)
    
    def OperationTest(self): 
            print('hello world')
            print('------------------')
    

最后提及:

QTreeWidgetQTreeView写到这里初步完成二者使用的基本介绍和比较,会发现二者在使用函数设置层面上看,基本没有任何区别,但QTreeView具有一个小功能:

​ 大家如果在QStandardItemModel的词条上双击,会发现文本会自动进入编辑模式,支持在此处进行修改,而不需要大家额外设置操作,这对于在某些场景下是非常方便的,这也是自己为什么会注意到QStandardItemModel的原因。

​ 下一个板块中会介绍QTreeView的另外一个好用的操作!!

再留下一个小坑,后续在更(●’◡’●)


2022.3.25 8:47我又来更新博客啦!!

四、QTreeView组件

​ 这个地方之所以再次提及TreeView组件,是因为我们需要借助于QFileSystemModel()实现利用文件资源管理创建树形结构操作。此操作在几乎所有的图形化界面中均有涉及,因此在此处学习尝试一下

​ 查阅一些博客,会发现大家在实现此功能时,往往会借助于QDirModel,但是当大家查阅QDirModel的官方文档时,它会将你引入名为QFileSystemModel,并提及现在更推荐使用QFileSytemModel,二者区别不大。

​ 如果大家直接使用QFileSystemModel,会发现他每次打开的均为默认目录,这显然不符合我们的要求:

model = QFileSytemModel() # 实例化QFileSystemModel对象

tree = QTreeView()
tree.setModel(model) # 模型应用

​ 于是,我们转向官方文档,搜索其中有没有设置打开路径的函数,发现setRootPath(),这正是我们所需要的可以改变打开路径的函数,但是应用后发现没有起作用,又搜索网络资源,在某位大神的博客中找到答案:

setRootPath()函数需要和setRootIndex()函数配合使用,才能真正生效

​ 故,代码发展到下面的样子:

model = QFileSystemModel() # 实例化QFileSystemModel对象

model.setRootPath(dir) # 设置QFileSystemModel打开的根目录(dir即为目标目录)

# 创建TreeView对象,应用QFileSystemModel
tree = QTreeView()
tree.setModel(model)

tree.setRootIndex(model.index(dir)) # 根目录生效

​ 打开特定的文件夹生成树形结构已经实现,但是没有做到与用户交互,所以搜索网络资源寻找弹出文件选择框选择文件夹的功能如何实现,找到QFileDialog

# 文件夹选择对话框弹出(函数可以将用户选择的文件路径返回)
dir = QFileDialog.getExistingDirectory(self,
									   r'文件选择',
									   r'C:/Users/腻味/Desktop' # 文件选择时默认打开的目录
)

​ 而后,我们又测试如何借助QFileSystemModel在指定的文件夹中生成文件夹,并在树形结构中实时更新:mkdir函数

​ 至此,QFileSystemModel部分的代码全部实现,看一下总的代码:

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt,QUrl,QDir
import sys

class TreeView(QMainWindow):

    def __init__(self):
        super(TreeView, self).__init__()
        self.initUI()

    def initUI(self):
        self.resize(600, 400)
        self.setWindowTitle('QTreeView')

        model = QFileSystemModel() # 实例化QFileSystemModel对象

        # 文件夹选择对话框弹出
        dir = QFileDialog.getExistingDirectory(self,
                                                    r'文件选择',
                                                    r'C:/Users/腻味/Desktop' # 文件选择时默认打开的目录
                                                    )
        model.setRootPath(dir) # 设置QFileSystemModel打开的根目录(用户选择的目录)

        # 创建TreeView对象,应用QFileSystemModel
        tree = QTreeView()
        tree.setModel(model)

        tree.setRootIndex(model.index(dir)) # 根目录生效

        # 在指定的文件夹内生成目录(后续可拓展删除和修改功能)
        model.mkdir(model.index(dir), 'DirTest')
        model.mkdir(model.index(dir), 'DirTest2')

        self.setCentralWidget(tree)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = TreeView()
    print(main.__doc__)
    main.show()
    sys.exit(app.exec_())

这一部分关于树形结构 QT for python,写到这里就基本完成,后续代码整理完善,会将代码和参考文献丢上来,学习过程中,参考内容较多,不能一一列举,侵删

参考文献:
附录代码:

​ 为方便大家测试使用,此处直接附录代码,可直接下载使用:

  • QTreeView测试代码:

    # -*-coding = utf-8-*-
    
    # Author:qyan.li
    # Date:2022/3/19 14:40
    # Topic:借助于QStandardItemModel和QTreeView实现树形结构创建
    # Reference:https://blog.csdn.net/cibiren2011/article/details/46467667
    
    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtCore import Qt, QModelIndex
    from PyQt5.QtGui import QStandardItemModel, QStandardItem
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QTreeView,
                                 QAbstractItemView, QHeaderView, QStyleFactory)
    
    class StandardItemModel(QMainWindow):
        def __init__(self,parent = None):
            super(StandardItemModel,self).__init__(parent)
    
            # 设置窗口标题,大小
            self.setWindowTitle('StandardItemModel')
            self.resize(520,300)
            # 初始化处理
            self.initUi()
    
        def initUi(self):
    
            # 设置表头相关信息
            model = QStandardItemModel(self) # 实例化对象
            model.setHorizontalHeaderLabels(['key','value'])
    
            # 添加条目
            item1 = QStandardItem('University')
            item2 = QStandardItem('UESTC')
            model.appendRow(item1)
            model.setItem(0,1,item2)
    
            model.setItem(1,0,QStandardItem('liqiyan'))
            model.setItem(1,1,QStandardItem('liqiyan'))
    
            model.insertRow(1,QStandardItem('Python'))
            model.insertRow(0,QStandardItem('code'))
    
            # model.removeRow(0)
            # model.removeColumn(1)
            # model.takeRow(1)
            # model.takeColumn(1)
            # model.takeItem(1,1)
    
            child1 = QStandardItem('Peking')
            child2 = QStandardItem('100')
            item1.appendRow(child1)
            item1.setChild(0,1,child2)
    
            child3 = QStandardItem('Tinguish')
            child4 = QStandardItem('98')
            item1.appendRow(child3)
            item1.setChild(1,1,child4)
    
            item1.insertRow(1,QStandardItem('cpp'))
    
            # 虽然每一个结点均为sIteM对象,但仅有前面的可以增加子节点
            child3.appendRow(QStandardItem('Student:qyanLi'))
            child3.setChild(0,1,QStandardItem('Test'))
            child3.appendRow(QStandardItem('Name:qyanLi'))
            child3.setChild(1,1,QStandardItem('Test1'))
    
            # 重要的概念:row 和 column,借助于此获取子节点
            item = child3.child(1,1)
            print(item.text())
    
            child3.appendRow(child3.clone()) # 拷贝功能,返回复制后的对象
    
            ## index为QT中非常重要的概念,很多时候会作为参数进行传入
    
            child3.insertRow(1,QStandardItem('qyanLi'))
            child3.setChild(1,1,QStandardItem('qyanLi'))
    
            ## model和parent函数可以获得对应的模型或者父节点
    
            ## remove和take的区别主要在于返回值的不同
            ## QStrandardItemModel每个(row,column)位置上均为子节点类型
            # child3.removeRow(1) # remove整行
            # child3.removeColumn(1)
            # child3.takeChild(1,1) # remove掉特定点
            # child3.takeColumn(1) # remove整列
            # child3.takeRow(1)
            
            ## 选中功能的实现
            # 选中功能的实现需要借助于TreeView类实现
    
            ## 使用QStandardItemModel注意点:
            '''
            1. 每个QStandardItemModel对象仅能使用一次,用过之后,后续不再生效
            2. setItem和setChild操作->在同一个Item下的子节点借助于坐标进行区分
            3. QStandardItemModel自带修改功能,但是不知道能不能用
            '''
    
            treeView = QTreeView(self)  # 实例化TreeView对象
            treeView.setModel(model)  # 将实例化后的对象应用于设计的model上
            # 调整第一列的宽度
            treeView.header().resizeSection(0, 160)
            # 设置成有虚线连接的方式
            treeView.setStyle(QStyleFactory.create('windows'))
            # 完全展开
            treeView.expandAll()
    
            # # 显示选中行的信息(借助于函数实现)
            # treeView.selectionModel().currentChanged.connect(self.onCurrentChanged)  # 参数传入函数对象
            treeView.selectionModel().currentChanged.connect(self.OperationTest)
    
            ## 设置完成后赋值语句的固定操作
            self.model = model
            self.treeView = treeView
            self.setCentralWidget(treeView)
    
        def OperationTest(self): # 借助于此种方式实现选中操作
            print('hello world')
            print('------------------')
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = StandardItemModel()
        window.show()
        sys.exit(app.exec())
    
    
  • QTreeWidget测试代码:

    # -*-coding = utf-8-*-
    
    # Author:qyan.li
    # Date:2022/3/19 10:03
    # Topic:借助于TreeWidget完成树形结构的增加,删除,修改的操纵
    
    from PyQt5.QtWidgets import *
    import sys
    
    class TreeWidget(QWidget): # 注意继承QWidget
        '''
        借助于QT-TreeWidget,完成树形结构的构建,结点的增加,删除和更改
        '''
    
        def __init__(self):
            super(TreeWidget, self).__init__()
            self.InitUI()
    
        def InitUI(self):
            # 设置窗口标题,大小
            self.setWindowTitle('TreeWidget应用示例')
            self.resize(400,300)
    
            # 实例化QTreeWidget对象,并初始化基本信息(标签数目,标签名称)
            self.tree = QTreeWidget()
            self.tree.setColumnCount(2)
            self.tree.setHeaderLabels(['key','value'])
    
            ## ---------------------QTreeWidget添加结点办法--------------------------
            # QTreeWidget中添加结点item(借助于构造方法)
            root1 = QTreeWidgetItem(self.tree)
            root1.setText(0,'UESTC')
            root1.setText(1,'成都')
    
            # QTreeWidget中添加结点item(借助于addTopLevelItem)
            root2 = QTreeWidgetItem()
            root2.setText(0,'Peking')
            root2.setText(1,'北京')
            self.tree.addTopLevelItem(root2)
            ## ---------------------QTreeWidget添加结点办法--------------------------
    
            ## ---------------------QTreeWidgetItem添加结点办法--------------------------
            # QTreeWidgetItem中添加结点(借助于构造方法)
            child11 = QTreeWidgetItem(root1)
            child11.setText(0,'score')
            child11.setText(1,'100')
    
            # QTreeWidgetItem中添加结点(借助于addChild)
            # 另外有addChildren(传入参数为itemList)
            child12 = QTreeWidgetItem()
            child12.setText(0,'subject')
            child12.setText(1,'Communication')
            root1.addChild(child12)
    
            # QTreeWidgetItem中添加结点(借助于insertChild(index,child)在指定位置插入child)
            # 另外有insertChildren(传入参数为itemList)
            child13 = QTreeWidgetItem()
            child13.setText(0,'Temperature')
            child13.setText(1,'27')
            root1.insertChild(0,child13)
            ## ---------------------QTreeWidgetItem添加结点办法--------------------------
    
            ## ---------------------QTreeWidgetItem删除结点办法--------------------------
            # 添加测试结点
            child21 = QTreeWidgetItem(root2)
            child21.setText(0,'score')
            child21.setText(1,'100')
    
            # QTreeWidgetItem删除结点(借助于*removeChild*方法,参数内部传入结点对象,返回值为None)
            # root2.removeChild(child21)
    
            # QTreeWidgetItem删除结点(借助于*takeChild*方法,参数内部传入int index,返回值为删除的结点对象)
            # root1.takeChild(2)
            # *takeChildren*函数内部不需要传入任何参数,删除此item下所有的子结点
            # root1.takeChildren()
            ## ---------------------QTreeWidgetItem删除结点办法--------------------------
    
            # 展开所有的结点
            self.tree.expandAll()
            # 设置激发事件
            self.tree.clicked.connect(self.onTreeClicked) # 单击item
            self.tree.doubleClicked.connect(self.doubleClicked) # 双击item
    
            # 代码必须存在,否则界面中不会进行显示
            mainLayout = QVBoxLayout(self)
            mainLayout.addWidget(self.tree)
    
        def onTreeClicked(self):
            # ---------------------------QTreeWidget定位方式--------------------
            item = self.tree.currentItem() # 当前所在item词条
            index = self.tree.currentColumn() # 当前所在的columnIndex
            print('key = {} value = {}'.format(item.text(0),item.text(1)))
            print('columnIndex = {}'.format(index))
    
            # ---------------------------QTreeWidgetItem定位方式---------------
            # (但是想不到任何的应用场景)
            flag = self.tree.currentItem().isSelected()
            print('isSelected:{}'.format(flag))
    
        # 代码测试
        def doubleClicked(self):
            print('hello world')
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        main = TreeWidget()
        # print(main.__doc__)
        main.show()
        sys.exit(app.exec_())
    
  • 文件树生成:

    # -*-coding = utf-8-*-
    
    # Author:qyan.li
    # Date:2022/3/18 10:51
    # Topic:
    # Reference:
    
    
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import Qt,QUrl,QDir
    import sys
    
    class TreeView(QMainWindow):
    
        def __init__(self):
            super(TreeView, self).__init__()
            self.initUI()
    
        def initUI(self):
            self.resize(600, 400)
            self.setWindowTitle('QTreeView')
    
            model = QFileSystemModel() # 实例化QFileSystemModel对象
    
            # 文件夹选择对话框弹出
            dir = QFileDialog.getExistingDirectory(self,
                                                        r'文件选择',
                                                        r'C:/Users/腻味/Desktop' # 文件选择时默认打开的目录
                                                        )
            model.setRootPath(dir) # 设置QFileSystemModel打开的根目录(用户选择的目录)
    
            # 创建TreeView对象,应用QFileSystemModel
            tree = QTreeView()
            tree.setModel(model)
    
            tree.setRootIndex(model.index(dir)) # 根目录生效
    
            # 在指定的文件夹内生成目录(后续可拓展删除和修改功能)
            model.mkdir(model.index(dir), 'DirTest')
            model.mkdir(model.index(dir), 'DirTest2')
    
            self.setCentralWidget(tree)
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        main = TreeView()
        print(main.__doc__)
        main.show()
        sys.exit(app.exec_())
    
    
  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值