QT
树形组件创建,增加,删除入门学习----绝对细致,一篇入门
Author:qyan.li
Date:2022.3.22
Topic:借助于python语言在QT中创建树形组件
一、前言:
QT
创建图形化界面时,经常会遇到需要创建树形组件的需求,比如在左侧创建"文件目 录"栏,这都会应用到树形组件。
QT for python
中常用的树形组件有两种,分别为TreeWidget
和TreeView
,这两种类都可以创建树形组件,但是特点不尽相同。
二、TreeWidget组件
TreeWidget
类是QT for python
创建树形结构的常用组件,在本部分主要针对于TreeWidget
构建树,增加、删除子节点的相关操作。
-
树构建基本配置:
-
窗口设置:
# 设置窗口标题,大小 self.setWindowTitle('TreeWidget应用示例') self.resize(400,300)
-
树基本设置:
# 实例化QTreeWidget对象,并初始化基本信息(标签数目,标签名称) self.tree = QTreeWidget() self.tree.setColumnCount(2) # 设置标签个数 self.tree.setHeaderLabels(['key','value']) # 设置标签内容
-
-
树(
TreeWidget
对象增加结点) 在讲述增加节点的内容之前,区分两个概念:
TreeWidget
和TreeWidgetItem
,二者是可以在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:
insertChild
和addChild
使用方法类似,insertChild
多index
参数,可以指定在特定位置插入- 针对于同一个父节点的所有子节点,
index
从零开始递增
-
-
设置节点信息
在"树的基本设置环节",提及树可以设置若干个标签用于指示树的信息,针对于节点,可以借助于setText()函数实现结点信息的设置
child13.setText(0,'Temperature') # index指代标签1或者2 child13.setText(1,'27')
写在这里的小总结:
为什么在这里写总结,因为结点的增加函数设置体现
QT for python
函数设置的思想:
- TreeWidget和TreeWidget概念的区别与分离
- 无论是增加还是删除函数,均设置多实现函数供用户选择(往往是默认和自定义<
insertChild
和addChild
>),这点和数据结构的构建思想很像,这点会在后续删除和TreeView操作中非常常见
-
结点(TreeWidgetItem对象删除结点)
-
removeChild()
函数root2.removeChild(child21) # 内部传入结点对象,返回值为None
-
takeChild()
函数root1.takeChild(2) # 传入待删除对象的index,返回值为被删除的对象本身
小Tips:
takeChildren()
函数,内部不需要传入任何参数,删除此item
下的所有子节点
小总结:
removeChild()
和takeChild()
函数均可以完成删除结点操作,区别在于返回值return
不同和传入参数不同 -
先挖一个小坑,后续再填((●’◡’●))
2022.3.24 15:39 来填坑啦!!但是好像又要留下另外一个坑
三、QTreeView组件
QTreeView
是QT for python
创建树形结构时常用的另一个组件,QTreeWidget
比QTreeView
更简单,但是灵活性没有QTreeView
高,QTreeView
应用StandardItemModel
模型,下面针对于该组件依旧阐述增加,删除,修改,选中四个方面的操作
-
树结构基本配置
-
初始化窗口基本信息
# 设置窗口标题,大小 self.setWindowTitle('StandardItemModel') self.resize(520,300)
-
初始化树结构基本信息
# 设置表头相关信息 model = QStandardItemModel(self) # 实例化对象 model.setHorizontalHeaderLabels(['key','value'])
-
-
QTreeView
增加条目-
借助于
appendRow()
方法item1 = QStandardItem('University') # 创建QStandardItem对象 model.appendRow(item1) # 将Item对象添加至Model对象中
小
Tips:
appendRow
方法不仅仅可以应用于Model
对象添加条目,Item
对象添加条目同样可以使用QStandardItemModel
和QStandardItem
对象的区别与QTreeWidget
中相同
-
借助于
insertRow()
方法model.insertRow(1,QStandardItem('Python')) model.insertRow(0,QStandardItem('code'))
小
Tips:
- 与
QtreeWidget
相同,insertRow
和appendRow
方法的区别在于是否可以自定义指定位置 insertRow
方法不仅仅适用于QStandardItenModel
对象,同样是适用于QStandardItem
对象
- 与
-
setItem
和setChild
方法 如果仔细观察,就会发现上述两种添加结点的方法只能在第一列添加内容(也就是本文测试中的
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
方法QStandardItemModel
和QTreeWidget
不同的一点在于,在QStandardItemModel
中,无论哪一个cloumn
下的词条内容均为Item
对象,string
类型的字符串是无法进行设置的- 个人发现,词条在进行添加时,同一个
Item
对象仅能使用一次,意味着每个词条仅能添加一次,后续使用会失效
-
-
QTreeView
删除结点
QTreeView
中无论QStandardItemModel
还是QStandardItem
的删除操作,都可以分为大致的几个类别进行讲解-
按照返回值类型
return type
删除操作按照返回值类型主要分为
take
和remove
两种类型的函数简单理解:
remove
仅进行删除操作,而take
函数会将被删去的结点返回回来 -
按照删除对象
删除操作中按照删除对象可以大致分为三类:删除
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方法适用于Model
,Child
方法适用于Item
-
-
QTreeView
常用函数-
child
函数child
函数可以用于获得某个结点下特定位置的子节点item = child3.child(1,1)
- 可以猜测,对应于
QStandardItemModel
模块存在item
函数
- 可以猜测,对应于
-
text
函数text
函数可以用于获得某个结点的内容,其中不需要传入任何参数text = child3.text()
-
model
和parent
函数model
和parent
函数用于获得某个结点,其所对应的model和父节点
-
-
激发事件设置
激发事件的设置需要借助于
QTreeView
treeView.selectionModel().currentChanged.connect(self.OperationTest) def OperationTest(self): print('hello world') print('------------------')
最后提及:
QTreeWidget
和QTreeView
写到这里初步完成二者使用的基本介绍和比较,会发现二者在使用函数设置层面上看,基本没有任何区别,但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
参考文献:QTreeWidget
参考:link:https://blog.csdn.net/jia666666/article/details/81668590QTreeWidget
参考:link:https://blog.csdn.net/weixin_42052836/article/details/80072709
QTreeWidget
参考:link:https://blog.csdn.net/u013541325/article/details/115655614QTreeWidgetItem
官方文档:link:https://doc.qt.io/qtforpython/PySide6/QtWidgets/QTreeWidgetItem.html#PySide6.QtWidgets.PySide6.QtWidgets.QTreeWidgetItem.treeWidget -
QTreeView
参考文献:QT
实现目录树参考:link:https://blog.csdn.net/q1302182594/article/details/45343693QStandardItemModel
参考:(14条消息) QTreeView 和 QStandardItemModel的使用_郭大懒人的博客-CSDN博客QTreeView
参考:link:https://blog.csdn.net/seniorwizard/article/details/110199352QStandardItemModel
官方文档:link:https://doc.qt.io/qtforpython/PySide6/QtGui/QStandardItem.htmlQDirModel
官方文档:QDirModel — 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_())