树部件(Tree Widget)是Qt Designer中 Item Widgets(Item-Based)部件中提供的一个Model/View便利部件,对应类为QTreeWidget类。
QTreeWidget类从QTreeView派生(继承关系请参考《PyQt学习随笔:Qt中Model/View相关的主要类及继承关系》),是一个Model/View便利类,提供一个树状管理数据的界面视图,为了快速支持树状视图展示数据,在类内使用了默认内置的树状模型保存数据项,每个数据项的类型都是QTreeWidgetItem对象。
不需要Model/View框架灵活性的开发人员可以很容易地使用QTreeWidget类来创建简单的树状分层列表,但使用QTreeView 与标准项目模型相结合则更灵活,因为QTreeView 与标准项目模型允许将数据的存储与其界面呈现分离。
一个QTreeWidget类对象包括一个界面上显示的部件框架及对应视口(关于视口请参考《PyQt(Python+Qt)学习随笔:QAbstractScrollArea的viewPort(视口)理解》)、头部标题项、数据项等部分组成。
树型部件的项是单独的类对象,这个类就是QTreeWidgetItem,树型部件的项用于保存树型部件显示数据的信息行。一个项对应的信息行可以包含几列数据,每列都可以包含一个文本标签和一个图标。
项通常由父项构造,父项可以是QTreeWidget(用于顶级项)对象或QTreeWidgetItem对象(用于树的二层及以下分支项)。
顶级项与树的下层项之间的主要区别在于顶级项没有parent()方法,这个区别可用于区分项之间的差异,并且在从树中插入和移除项时有助于了解项的情况。
默认情况下,项的状态是可用(enabled)、可选择(selectable)、可选中(checkable)的,并且可以作为拖放操作的源。可以通过使用适当的值调用setFlags()来更改每个项的标志(关于项的标记请参考《PyQt(Python+Qt)学习随笔:Model中项的标记flags取值及枚举类型Qt.ItemFlag》)。
QTreeWidgetItem的构造方法如下:
以上构造方法可以单独构建项,也可以构建项之后将项插入到对应部件的对应位置。相关参数说明如下:
其类型为枚举类型QTreeWidgetItem.ItemType,树型部件中的标准QTreeWidgetItem项对应的取值为QTreeWidgetItem.Type(对应整数0),如果开发者需要从QTreeWidgetItem派生自定义项,则需要使用项的Type值大于等于QTreeWidgetItem.UserType(对应整型1000)的值,使用不同的type值的目的是为了提供给开发者对自定义项进行诸如排序等特殊处理。
QTreeWidgetItem项可以通过flags()返回项的标记,返回值类型为类型Qt.ItemFlags,也可通过setFlags(Qt.ItemFlags flags)来设置项的标记,通过设置标记可以确认象是否可以进行选中、是否可编辑、是否可拖拽、是否支持复选框进行复选操作、是否可用等。具体项可以设置的属性可以参考《PyQt(Python+Qt)学习随笔:Model中项的标记flags取值及枚举类型Qt.ItemFlag》的介绍。
项的方法isDisabled()用于判断项是否可用,返回True表示项禁用,返回False可用。
注意:项的disable设置方法只能通过setFlags方法进行。
项方法isFirstColumnSpanned()用于返回是否显示时项的第一列数据跨越所有列,为True即只显示第一列数据,且占用所有列的位置,为False则显示所有列数据。
可以调用方法setFirstColumnSpanned(bool span)来改变该属性。
项可以隐藏不显示,通过方法isHidden()判断项是否隐藏,通过setHidden(bool hide)方法设置项是否隐藏。
如果flag设置项允许选择,则项可以被选中,下图顶层第二项为选中状态:
项是否选中可以通过项方法isSelected()来判断,要设置项的选中状态可以通过项的setSelected(bool select)方法来设置。
项构建以后,可以通过addChild(QTreeWidgetItem child)方法在该项中的子节点最后加入另一个项child作为该项的子项,也可以通过addChildren(iter[QTreeWidgetItem] children)将一个迭代类型children中的多个项加入到该项的子项最后。
如果要插入的项原来已经在QTreeWidgetItem对象中,则该项不会重复加入,如果是多项中有部分项已经在QTreeWidgetItem对象中,则这部分项不会重复加入,其他项则可以加入。
除了追加子项到子项列表最后之外,可以通过insertChild(int index,QTreeWidgetItem child)方法在该项中的子项列表的index位置插入child对应项,该位置及其后位置的项自动后移,也可以通过insertChildren(int index, (iter[QTreeWidgetItem] children)将一个迭代类型children中的多个项从index位置开始顺序插入,原位置的项自动后移。
树型部件中的项下可以有子项,如果存在子项,则父项的节点是否显示展开或折叠子项的提示符由属性childIndicatorPolicy控制。
childIndicatorPolicy属性的类型为枚举类型QTreeWidgetItem.ChildIndicatorPolicy ,对应取值及含义如下:
childIndicatorPolicy属性默认值为DontShowIndicatorWhenChildless,可以通过childIndicatorPolicy()和setChildIndicatorPolicy(QTreeWidgetItem.ChildIndicatorPolicy policy)来访问。
如果一个项有子项,可以调用setExpanded(bool expand)方法来展开或折叠其子项,项的子项是否折叠可以通过isExpanded()方法来判断。
可以通过child(int index) 方法获取项的子项列表中序号为index的项,注意index取值从0开始。子项的个数可以通过childCount()方法获取,index的值必须小于子项的个数,否则返回None。
项的父项可以通过parent() 方法返回,但项如果是顶级项,parent()方法返回None,而不是返回对应的QTreeWidget对象,如果要返回项对应的QTreeWidget对象,可用treeWidget()方法,这个方法对顶级项和非顶级项都是一样的。
可通过indexOfChild(QTreeWidgetItem child)方法返回项下子项child的位置索引,索引值从0计数,如果child对应项未在当前项的子项中,则返回-1。
removeChild是根据子项对象从项中删除子项,调用方法为removeChild(QTreeWidgetItem child),操作是从项中删除child对应子项,注意该方法无返回,如果对应子项不存在也不报错。
removeChild是根据子项对象从项中删除子项,但无返回,takeChild是根据子项的位置索引来删除子项,同时将删除的子项返回。调用方法为:
QTreeWidgetItem takeChild(int index)
如果执行失败,返回None。
takeChildren()是将当前项的所有子项都删除,并将所有子项存储到一个列表中返回,如果当前项没有子项,则返回空列表。
sortChildren是对项的下层子项按指定列的文本进行排序,调用语法:
sortChildren(int column, Qt.SortOrder order)
Qt.SortOrder为枚举类,有两个常量值,分别为:AscendingOrder升序,对应数值为0,DescendingOrder为降序,对应数值为1。
treeWidget()方法返回项所在树型部件对象,如果项没有插入到树型部件中,则返回None。
项的columnCount方法用于返回项中数据的列数,项数据的列数是在项中实际存储的数据列数,当项构造时传入数据或调用setText(int column,str text)方法增加数据时会改变项的列数。
树型部件QTreeWidget中的QTreeWidgetItem项中可以有多列数据,每列数据可以根据列位置进行访问。项中列数据的访问方式有两种:
可以通过icon(int column)来访问项中指定列的图标,通过setIcon(int column, QIcon icon)来设置项中指定列的图标。
如下面代码将顶层第一项第5列的图标设置为指定文件:
self.treeWidget.topLevelItem(0).setIcon(4,QtGui.QIcon(r'F:\小图标\动物\动物-025.gif'))
项中每列数据都可以单独设置复选状态,如图顶层第一项第一列设置了复选状态:
项中列的复选状态可以通过checkState(int column)来获取,如果要改变项中指定列的复选状态可以调用setCheckState(int column,Qt.CheckState state)来实施。
项中的列还可以:
除了从父类继承的属性外,QTreeWidget在Designer中只有一个属性columnCount属性,另外还有个属性在Designer中没有的属性topLevelItemCount。
树型部件中的每个项有一个或多个文字标签或其他装饰符(如图标),这些内容每个显示为一列。QTreeWidget的columnCount属性用于控制和保存树型部件窗口展示的每项列数,其缺省值为1。在将项添加到树型部件之前,必须使用setColumnCount()设置显示的列数。项的列数可以通过columnCount()函数获取。
下图是在Designer中设置columnCount为3时的截图:
在上面介绍QTreeWidgetItem的columnCount属性时,说明了QTreeWidgetItem和QTreeWidget的columnCount属性之间的关系,在此不再重复。
topLevelItemCount属性是一个只读属性,用于保存树型部件中顶层项的个数,可以通过topLevelItemCount()方法获取属性值,缺省值为0。
当树型部件中顶层项的数目变化时,topLevelItemCount自动跟随变化。
树型部件便利类QTreeWidget的主要方法老猿将其归纳为五大类,分别是构造方法、项操作访问方法、与项位置相关访问方法、顶层项操作和访问方法和部件头访问操作方法。
QTreeWidget的构造方法非常简单:
QTreeWidget(QWidget parent = None)
parent是父对象,一般设置为QTreeWidget对象部署的窗口部件。
在前面第二部分介绍了树型部件中的项QTreeWidgetItem类的属性和方法,除了项对象本身的操作方法外,在树型部件QTreeWidget类中也提供了项(包括顶层项)的操作访问方法。本部分介绍通用的项操作方法。
当前项是指当前鼠标和键盘焦点所在项,在项可以进行选择操作时,当前项可以是选中状态,也可以是未选中状态,选中项也不一定是当前项。与当前项相关的方法包括:
树型部件的currentColumn()方法返回当前项中得到焦点的列索引值,从0开始。如果没有当前列,则返回-1。
在树型部件根据选择模式的设置,只要选择模式不是NoSelection(关于选择模式继承自QAbstractItemView,请参考《PyQt(Python+Qt)学习随笔:QAbstractItemView的selectionMode属性》),则可以通过操作选中部件中的项。选中的项可以通过方法selectedItems()方法返回,其返回值为一个列表,列表中的每个元素是一个选中的QTreeWidgetItem项实例。
在树型部件中,可以根据文本、搜索列以及匹配模式来搜索满足条件的项,调用语法:
list[QTreeWidgetItem] findItems( str text, Qt.MatchFlags flags, int column = 0)
返回值为所有满足条件的项构成的列表,如果没有找到匹配项,返回空列表。
Qt.MatchFlags的取值及含义请参考《PyQt(Python+Qt)学习随笔:Model/View中的枚举类 Qt.MatchFlag的取值及含义》。
可以对树型部件QTreeWidget中的项按照指定列进行排序,调用语法:
sortItems(int column, Qt.SortOrder order)
Qt.SortOrder为枚举类,有两个常量值,分别为:AscendingOrder升序,对应数值为0,DescendingOrder为降序,对应数值为1。
在树型部件QTreeWidget中,有三种方法触发进行项数据的编辑。
在《PyQt(Python+Qt)学习随笔:QAbstractItemView的editTriggers属性以及平台编辑键(platform edit key )》介绍了QAbstractItemView可以通过设置editTriggers来触发编辑,而QTreeWidget是QAbstractItemView类派生的,继承了该属性,因此通过设置该属性为非NoEditTriggers的值即可触发编辑。
editTriggers可以在Qt Designer中设置,如图:
也可以调用setEditTriggers(EditTriggers triggers)来触发,关于EditTriggers 枚举类请参考PyQt(Python+Qt)学习随笔:QAbstractItemView的editTriggers属性以及平台编辑键(platform edit key )》。
QTreeWidget提供了进行项编辑的方法editItem,调用语法如下:
editItem(QTreeWidgetItem item, int column = 0)
上面介绍editItem时说明了editItem只能触发一次编辑,可以说进入临时编辑状态,一旦退出编辑除非再通过相关方式触发编辑否则项不可再编辑。与此相对应,QTreeWidget还提供了一种一旦打开编辑状态就可以随时再次编辑,除非显示关闭编辑状态,这种方式就是打开持久编辑器。调用方法如下:
openPersistentEditor(QTreeWidgetItem item, int column = 0)
该方法没有返回值。
在前面介绍了QTreeWidgetItem项的isFirstColumnSpanned()方法,该方法用于返回是否显示时项的第一列数据跨越所有列,并可以调用方法setFirstColumnSpanned(bool span)来改变该属性。
在树型部件QTreeWidget中也有类似方法,分别是isFirstItemColumnSpanned和setFirstItemColumnSpanned,调用方法如下:
这两个方法与项方法isFirstColumnSpanned、setFirstColumnSpanned的作用完全相同,就是方法名和参数有所区别,由于是部件方法,参数中必须指定项,而isFirstColumnSpanned、setFirstColumnSpanned本身是项的实例方法,无需指定项。
在4.1部分介绍了树型部件QTreeWidget方法中与项相关的方法,上述方法适用于所有树型部件的项,除了这些方法之外,树型部件QTreeWidget类还提供了部分与顶层项相关的访问和操作方法。
topLevelItem方法根据位置索引取指定位置的顶层项,调用语法如下:
QTreeWidgetItem topLevelItem(int index)
该方法严格来说不属于顶层项的方法,只是只有一个方法且确实能通过它方法顶层项,因此在此还是归并到顶层项的方法中。
我们知道在数据结构上来说,任何树都是有根节点的,但我们在QTreeWidget对象中并没有看到界面上展示一个根节点,在QTreeWidget对象和数据项构建时也没有指定这样的根节点,但实际上这个根节点还是存在的,只是不可见,且该节点是树型部件创建之后就自动创建,无论是否存在顶层项。
QTreeWidget提供了访问该隐形根节点的方法,调用语法如下:
QTreeWidgetItem invisibleRootItem()
这个方法其实用途不大,主要是可以用于递归访问树的所有节点,递归不是从topLevelItem开始,而是从根节点开始,所有节点的类型都是QTreeWidgetItem ,可以用QTreeWidgetItem 的child方法逐层递归访问。
QTreeWidget对象创建后,是没有任何项的,要给部件增加项,首先要增加顶层项。顶层项的增加有三类方法,一类是在前面介绍的QTreeWidgetItem中的构造方法构造项时,直接将QTreeWidget对象作为参数传递进去;第二类是利用上面介绍的隐形根节点,在隐形根节点下面调用QTreeWidgetItem相关方法增加子项即可;第三类就是直接调用QTreeWidget相关方法。
树型部件构建以后,可以通过addTopLevelItem(QTreeWidgetItem item)方法在部件中顶层项的最后加入另一个项item项的顶层项,也可以通过addTopLevelItems(iter[QTreeWidgetItem] items)将一个迭代类型items中的多个项加入到顶层项的最后。
如果要插入的项原来已经在QTreeWidget的项中,则该项不会重复加入,如果是多项中有部分项已经在QTreeWidget的项中,则这部分项不会重复加入,其他项则可以加入。
除了追加顶层项到顶层项最后之外,可以通过insertTopLevelItem(int index,QTreeWidgetItem item)方法在部件中顶层项列表的index位置插入item对应项,该位置及其后位置的项自动后移,也可以通过insertTopLevelItems(int index, (iter[QTreeWidgetItem] items)将一个迭代类型items中的多个项从index位置开始顺序插入,原位置的项自动后移。
通过调用树型部件的indexOfTopLevelItem方法可以获得对应项在顶层项的位置,调用语法如下:
int indexOfTopLevelItem(QTreeWidgetItem item)
如果对应项未在顶层项中,则返回-1。
树型部件的takeTopLevelItem方法可以从树型部件中删除对应项的节点并返回该项,调用语法如下:
QTreeWidgetItem takeTopLevelItem(int index)
如果对应位置无顶层项,则返回None。
项在树型部件中展示时,其界面上面的项称为上项,其界面下面的项称为下项,上项和下项与项不一定是同一层级的。如下图:
上图中蓝色选中的项(d:\)是当前项,其上项为黄色标记的项(c:\Python),其下项是蓝色框标记的项(d:\work)。这在Qt文档中是没有说明的,老猿是经过验证确认的,具体验证过程请参考《PyQt(Python+Qt)学习随笔:树型部件QTreeWidget的itemAbove、itemBelow方法作用探究》。
QTreeWidget的itemAt方法通过视口内的坐标点获取对应坐标位置的项,相关调用方法如下:
通过该方法可以获取到视口上对应坐标所在的项,如果对应坐标位置无项则返回None。
树型部件的visualItemRect方法可以返回参数指定项在视口矩形
QRect visualItemRect( QTreeWidgetItem item)
当项在树型部件中不可见时,返回值为一个空矩形(一个没有矩形坐标、长宽属性的QRect()对象)。
树型部件窗口可以有一个标题头,其中包含部件中每个列的节(即标题)。QTreeWidget的标题属性包括两部分,一部分是标题项,一部分标题头相关属性。
标题头实际上是一个QTreeWidgetItem项,因此标题项相关的属性就是QTreeWidgetItem想的属性,相关属性的访问就通过QTreeWidgetItem的方法去访问。树型部件提供了这个项的访问方法headerItem和setHeaderItem。调用语法如下:
如果需要改变标题,可以通过项的setText方法进行。如:
self.treeWidget.headerItem().setText(0, "文件或目录名")
self.treeWidget.headerItem().setText(1, "类型")
self.treeWidget.headerItem().setText(2, "大小")
除了用上面的方法根据标题项设置标题文本外,还可以使用setHeaderLabel改变标题首列的标题文本,也可以使用setHeaderLabels改变从首列开始的多个列的标题文本,具体列数看参数传递列表的元素个数。调用语法如下:
示例代码:
self.treeWidget.setHeaderLabels(("文件或目录名",'类型'))
QTreeWidget标题相关属性可以在Designer中设置,如图:
这些属性是从QTreeView继承的。关于这些属性的使用,请参考《PyQt(Python+Qt)学习随笔:QTreeView的标题表头header相关属性》。
QTreeWidget树型部件是一种内置存储model的便利类,它可以方便的呈现树型数据。QTreeWidget构造后,自动创建隐形根节点,隐形根节点不可见但可访问,隐形根节点下的子项就是树型部件的顶层项,顶层项创建后可以加入到树型部件中,顶层项可以继续添加子项,子项还可以再添加子项。相关项可以选中、删除和访问,部分操作能触发特定的信号,通过这些信号可以确认操作影响的数据。
老猿关于PyQt的付费专栏《使用PyQt开发图形界面Python应用》只需要9.9元,该部分与第十五章的内容基本对应,但同样内容在付费专栏上总体来说更详细、案例更多。本节内容对应付费专栏的《第二十二章、 Model/View便利类树型部件QTreeWidget》。如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。