文章目录
- 一、QTreeWidget介绍与常用函数
-
- [1.1 树结构的初始化](#1.1 树结构的初始化)
- [1.2 递归读取完整树结构](#1.2 递归读取完整树结构)
- 二、QTreeWidget事件绑定
-
- [2.1 单/双击事件](#2.1 单/双击事件)
- [2.2 勾选状态事件(勾选框)](#2.2 勾选状态事件(勾选框))
- [2.3 右击菜单栏](#2.3 右击菜单栏)
- [2.4 两QTreeWidget滑轮同步滑动](#2.4 两QTreeWidget滑轮同步滑动)
- 三、封装改写QTreeWidget
一、QTreeWidget介绍与常用函数
QTreeWidget是 PyQt 中用于显示树形结构数据的控件,继承自 QTreeView。它通过节点和子节点的层级关系展示数据,常用于文件浏览器、目录结构或分类管理界面。常用函数如下
项初始化
| 函数 | 含义 |
|---|---|
| clear() | 清空所有节点(包括子节点),释放内存 |
| setColumnCount(int columns) | 设置树的列数(默认1列) |
| setHeaderLabels(QStringList labels) | 设置列标题 |
| setColumnWidth(0, 200) | 设置列宽(第一列宽度200) |
| header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) | 列宽自适应 |
| QTreeWidgetItem(root_tree) | 节点加载root_tree上 |
| setText(0, "根节点") | 设置内容 |
| setIcon(0, QIcon("")) | 设置图片 |
| setCheckState(0, QtCore.Qt.Unchecked) | 设置选中状态(勾选框) |
| addTopLevelItem(QTreeWidgetItem *item) | 在顶层添加一个项 |
| addTopLevelItems(QList<QTreeWidgetItem *> items) | 批量添加顶层项 |
| insertTopLevelItem(int index, QTreeWidgetItem *item) | 在指定位置插入顶层项 |
| takeTopLevelItem(int index) | 移除但不删除指定位置的顶层项 |
| addChild(QTreeWidgetItem *parent, QTreeWidgetItem *child) | 为父项添加子项 |
| insertChild(int index, QTreeWidgetItem *parent, QTreeWidgetItem *child) | 在父项的指定位置插入子项 |
| setHeaderItem(QTreeWidgetItem *item) | 自定义标题项(可设置字体/颜色等) |
| setCurrentItem(QTreeWidgetItem *item) | 设置当前选中项 |
项的相关信息获取
| 函数 | 含义 |
|---|---|
| currentItem() | 获取当前选中的项(控件对象) |
| columnCount() | 获取当前列数(控件对象) |
| invisibleRootItem().childCount() | 获取根节点的子节点数(控件对象) |
| invisibleRootItem().child(i) | 获取根节点的某个子节点(控件对象) |
| childCount() | 获取当前节点的子节点数(invisibleRootItem()返回的对象) |
| child(i) | 获取当前节点的某个子节点 |
| checkState(0) | 获取选中状态(参数表示哪列) |
| text(0) | 获取某列内容(参数表示哪列) |
| parent().text(column) | 获取父节点内容(参数表示哪列) |
| selectedItems() | 获取所有选中的项列表 |
| indexFromItem(QTreeWidgetItem *item) | 获取项的模型索引 |
| findItems(QString text, Qt::MatchFlags flags, int column) | 查找匹配项 |
| topLevelItem(int index) | 获取指定索引的顶层项 |
| topLevelItemCount() | 获取顶层项数量 |
| itemAbove(QTreeWidgetItem *item) | 获取上一个同级项 |
| itemBelow(QTreeWidgetItem *item) | 获取下一个同级项 |
其他相关函数
| 函数 | 含义 |
|---|---|
| expandItem(const QTreeWidgetItem *item) | 展开指定节点(显示子节点) |
| collapseItem(const QTreeWidgetItem *item) | 折叠指定节点(隐藏子节点) |
| expandAll() | 全部展开 |
| collapseAll() | 全部收起 |
| scrollToItem(QTreeWidgetItem *item) | 滚动到指定项 |
| setIndentation(int indent) | 设置缩进像素值 |
| setRootIsDecorated(bool show) | 显示/隐藏根项展开标记 |
| sortItems(int column, Qt::SortOrder order) | 按指定列排序 |
| setSortingEnabled(bool enable) | 启用/禁用排序功能 |
| setItemWidget(QTreeWidgetItem *item, int column, QWidget *widget) | 为项设置自定义控件 |
| visualItemRect(QTreeWidgetItem *item) | 获取项在视图中的显示区域 |
1.1 树结构的初始化
python
# 创建QTreeWidget对象
self.tree_widget = QTreeWidget()
# 设置列数、列标题、列宽
self.tree_widget.setColumnCount(2)
self.tree_widget.setHeaderLabels(["名称", "类型"])
self.tree_widget.setColumnWidth(0, 200)
self.tree_widget.setColumnWidth(1, 150)
# self.tree_widget.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) # 列宽自适应
# 初始化表格
root_item1 = QtWidgets.QTreeWidgetItem(self.tree_widget)
root_item1.setText(0, "root1")
root_item1.setText(1, "dir1")
# root_item1.setCheckState(0, QtCore.Qt.Unchecked) # 设置勾选状态(不勾选) 可省略
# 同上
root_item2 = QtWidgets.QTreeWidgetItem(["root2", "dir2"])
self.tree_widget.addTopLevelItem(root_item2)
# 第二列增加一个按钮
root_item3 = QtWidgets.QTreeWidgetItem(["root3", "dir3"])
btn = QtWidgets.QPushButton("Button")
self.tree_widget.addTopLevelItem(root_item3)
self.tree_widget.setItemWidget(root_item3, 1, btn) # 如果为子节点,则root_item3改为子节点对象
# 子节点
root_item4 = QtWidgets.QTreeWidgetItem(root_item3, ["root4", "dir4"])
self.tree_widget.expandAll() # 所有节点展开
self.tree_widget.collapseAll() # 所有节点收起
1.2 递归读取完整树结构
python
# 创建QTreeWidget实例
tree_widget = QTreeWidget() # 初始化...略
# 获取信息
root = self.tree_widget.invisibleRootItem()
for iin range(root.childCount()):
# 获取当前节点信息、选中状态
item = root.child(i)
print(item.text(0), item.text(1), item.checkState(0))
# 获取子节点信息
for j in range(item.childCount()):
child_item = item.child(j)
print(child_item.text(0), child_item.text(1), child_item.checkState(0))
二、QTreeWidget事件绑定
| 函数 | 含义 |
|---|---|
| itemClicked.connect() | 项被点击 |
| itemDoubleClicked.connect() | 项被双击 |
| itemChanged.connect() | 勾选状态变化事件(勾选框) |
| setContextMenuPolicy(QtCore.Qt.CustomContextMenu) customContextMenuRequested.connect(self.right_clicked_tree) | 右击事件 |
| valueChanged.connect() | 滚轮滚动 |
| itemSelectionChanged.connect() | 选中项改变 |
| itemSelectionChanged.connect() | 项选择变化 |
| itemExpanded.connect() | 项展开 |
| itemCollapsed.connect() | 项折叠 |
| itemChanged.connect() | 项内容编辑 |
2.1 单/双击事件
python
# 单击事件
self.tree_widget.itemClicked.connect(self.OnClickedItem)
# 双击
# self.tree_widget.itemDoubleClicked.connect(self.clicked_item)
def OnClickedItem(self, item, column): # item: 点击项 column:列
if item:
# 当前节点信息
item_text = item.text(column) # item.text(0)
# 获取父节点信息
if item.parent():
parent_text = item.parent().text(column)
# 获取子节点信息
for child_index in range(item.childCount()):
child_text = item.child(child_index).text(column)
2.2 勾选状态事件(勾选框)
python
self.tree_widget.itemChanged.connect(self.OnItemChanged)
def OnItemChanged(self, item, column): # item: 点击项 column:列
if item.checkState(0) == Qt.Checked:
print(f"项目 {item.text(0)} 被选中")
else:
print(f"项目 {item.text(0)} 取消选中")
多层级勾选框:对于多层级勾选事件,可能有这么个需求,勾选该节点时,该节点的子节点都勾选上,当前层的如果都勾选上了,则该节点的父节点也自动勾选上,实现代码如下
python
self.tree_widget.itemChanged.connect(self.OnItemChanged)
def OnItemChanged(self, item, column):
try:
self.tree_widget.itemChanged.disconnect(self.on_item_change)
except Exception as e:
pass
# 处理子节点
self.DeepOnItemChangeChild(item)
# 处理父节点
self.DeepOnItemChangeParent(item.parent())
self.tree_widget.itemChanged.connect(self.on_item_change)
# 遍历处理子节点
def DeepOnItemChangeChild(self, item):
check_flag = QtCore.Qt.Checked if item.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Unchecked
for i in range(item.childCount()):
child = item.child(i)
child.setCheckState(0, check_flag)
self.DeepOnItemChangeChild(child)
# 遍历处理父节点
def DeepOnItemChangeParent(self, item):
if item:
item.setCheckState(0, QtCore.Qt.Unchecked)
for i in range(item.childCount()):
if item.child(i).checkState(0) != QtCore.Qt.Checked:
break
else:
item.setCheckState(0, QtCore.Qt.Checked)
self.DeepOnItemChangeParent(item.parent())
2.3 右击菜单栏
如实现右击弹出菜单栏,将当前选选项移除
python
# 右击绑定事件
self.tree_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tree_widget.customContextMenuRequested.connect(self.RightClickedTree)
def RightClickedTree(self, pos):
item = self.tree_widget.itemAt(pos)
if item:
menu = QMenu(self.tree_widget)
remove_item = QAction("remove", self.tree_widget)
remove_item.triggered.connect(lambda: self.RemoveItem(item)) # 移除事件绑定
menu.addAction(remove_item)
menu.exec_(QtGui.QCursor.pos())
# 移除当前项
def RemoveItem(self, item):
if item.parent():
item.parent().removeChild(item)
else:
index = self.tree_widget.indexOfTopLevelItem(item)
self.tree_widget.takeTopLevelItem(index)
一般来说,直接使用Menu,对选项右击也会出发事件,这是需要对menu进行部分函数封装改写,忽略右击对菜单栏选项操作
python
class MyMenu(QtWidgets.QMenu):
# 忽略右击选项
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
super().mousePressEvent(event)
else:
event.ignore()
2.4 两QTreeWidget滑轮同步滑动
python
# 创建并初始化
self.tree1 = QtWidgets.QTreeWidget()
self.tree2 = QtWidgets.QTreeWidget()
for i in range(100):
QtWidgets.QTreeWidgetItem(self.tree1).setText(0, str(i))
QtWidgets.QTreeWidgetItem(self.tree2).setText(0, str(i))
# 获取水平滚动条
scoll1 = self.tree1.verticalScrollBar()
scoll2 = self.tree2.verticalScrollBar()
# 同步滚动
scoll1.valueChanged.connect(lambda val: scoll2.setValue(val))
scoll2.valueChanged.connect(lambda val: scoll1.setValue(val))
三、封装改写QTreeWidget
对于开发,很多功能可能会重复使用,这是可以继承改写原有的类,方便后期开发
python
class MyQTreeWidgetItem(QTreeWidget):
'''
title_list: 列标题栏
width_list: 列宽
select_flag: 是否启动勾选框相应事件绑定
'''
def __init__(self, title_list, width_list=[], select_flag=False):
super().__init__()
self.setColumnCount(len(title_list))
self.setHeaderLabels(title_list)
for width in width_list:
self.setColumnWidth(width, width)
self.itemChanged.connect(self.onItemChange) if select_flag else None
def onItemChange(self, item, column):
try:
self.itemChanged.disconnect(self.onItemChange)
except Exception as e:
pass
# 处理子节点
self.deepOnItemChangeChild(item)
# 处理父节点
self.deepOnItemChangedParent(item.parent())
self.itemChanged.connect(self.onItemChange)
# 递归处理子节点
def deepOnItemChangeChild(self, item):
check_flag = QtCore.Qt.Checked if item.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Unchecked
for i in range(item.childCount()):
child = item.child(i)
child.setCheckState(0, check_flag)
self.deepOnItemChangeChild(child)
# 递归处理父节点
def deepOnItemChangedParent(self, item):
if item:
item.setCheckState(0, QtCore.Qt.Unchecked)
for i in range(item.childCount()):
if item.child(i).checkState(0) == QtCore.Qt.Checked:
item.setCheckState(0, QtCore.Qt.Checked)
break
self.deepOnItemChangedParent(item.parent())
# 获取选中的内容(勾选框)
def getSelectList(self):
select_list = []
root = self.invisibleRootItem()
for i in range(root.childCount()):
if root.child(i).checkState(0) == QtCore.Qt.Checked:
select_list.append(root.child(i).text(0))
return select_list
# 添加节点
"""
root: 创建节点属于哪个节点的子节点
message_list: 展示的内容
select_flag: 是否启动勾选框
"""
@staticmethod
def addItem(root, message_list, select_flag=False):
child = QTreeWidgetItem(root)
for i in range(len(message_list)):
child.setText(i, message_list[i])
child.setCheckState(0, QtCore.Qt.Unchecked) if select_flag else None
return child