当前位置: 首页 > news >正文

PyQt(12)TreeWidget与TreeView对比

1.TreeWidget实现树形列表 勾选与右键菜单

import sys from PyQt5.QtWidgets import ( QApplication, QMainWindow, QTreeWidget, QTreeWidgetItem, QMenu, QAction, QMessageBox ) from PyQt5.QtCore import Qt class TreeWidgetDemo(QMainWindow): def __init__(self): super().__init__() self._is_updating = False # 防递归死循环标志 self.init_ui() def init_ui(self): self.setWindowTitle("QTreeWidget 勾选联动示例") self.setGeometry(100, 100, 600, 400) # 1. 创建 TreeWidget self.tree_widget = QTreeWidget(self) self.setCentralWidget(self.tree_widget) self.tree_widget.setColumnCount(1) self.tree_widget.setHeaderLabel("文件目录") # 2. 启用节点勾选功能(支持半选状态) self.tree_widget.setSelectionMode(QTreeWidget.ExtendedSelection) self.tree_widget.setItemsExpandable(True) self.tree_widget.setExpandsOnDoubleClick(True) # 3. 构建树形节点(带勾选框,支持半选) self._create_tree_items() # 4. 绑定右键菜单和勾选变化事件 self.tree_widget.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_widget.customContextMenuRequested.connect(self.show_right_menu) self.tree_widget.itemChanged.connect(self.on_item_check_changed) def _create_tree_items(self): """创建树形节点,启用勾选框(支持半选)""" # 根节点1:文档 root1 = QTreeWidgetItem(self.tree_widget, ["文档"]) # 启用勾选+半选功能 root1.setFlags(root1.flags() | Qt.ItemIsUserCheckable) root1.setCheckState(0, Qt.Unchecked) # 子节点1-1/1-2 child1_1 = QTreeWidgetItem(root1, ["简历.docx"]) child1_1.setFlags(child1_1.flags() | Qt.ItemIsUserCheckable) child1_1.setCheckState(0, Qt.Unchecked) child1_2 = QTreeWidgetItem(root1, ["报告.pdf"]) child1_2.setFlags(child1_2.flags() | Qt.ItemIsUserCheckable) child1_2.setCheckState(0, Qt.Unchecked) # 根节点2:图片(多级子节点示例) root2 = QTreeWidgetItem(self.tree_widget, ["图片"]) root2.setFlags(root2.flags() | Qt.ItemIsUserCheckable) root2.setCheckState(0, Qt.Unchecked) # 二级节点:风景 child2_1 = QTreeWidgetItem(root2, ["风景"]) child2_1.setFlags(child2_1.flags() | Qt.ItemIsUserCheckable) child2_1.setCheckState(0, Qt.Unchecked) # 三级节点:山川/湖泊 child2_1_1 = QTreeWidgetItem(child2_1, ["山川.jpg"]) child2_1_1.setFlags(child2_1_1.flags() | Qt.ItemIsUserCheckable) child2_1_1.setCheckState(0, Qt.Unchecked) child2_1_2 = QTreeWidgetItem(child2_1, ["湖泊.jpg"]) child2_1_2.setFlags(child2_1_2.flags() | Qt.ItemIsUserCheckable) child2_1_2.setCheckState(0, Qt.Unchecked) self.tree_widget.expandAll() def on_item_check_changed(self, item, column): """勾选状态变化联动处理(核心逻辑)""" if self._is_updating or column != 0: # 避免递归死循环/只处理第0列 return self._is_updating = True # 锁定更新 try: current_state = item.checkState(0) # 1. 父节点变化:同步所有子节点 self._set_child_check_state(item, current_state) # 2. 子节点变化:更新所有父节点(半选/全选/未选) self._update_parent_check_state(item.parent()) finally: self._is_updating = False # 解锁更新 def _set_child_check_state(self, parent_item, check_state): """递归设置父节点下所有子节点的勾选状态""" child_count = parent_item.childCount() for i in range(child_count): child = parent_item.child(i) child.setCheckState(0, check_state) self._set_child_check_state(child, check_state) # 递归处理孙子节点 def _update_parent_check_state(self, parent_item): """递归更新父节点的勾选状态(全选/半选/未选)""" if not parent_item: # 无父节点则终止 return child_count = parent_item.childCount() checked_count = 0 unchecked_count = 0 # 统计子节点勾选状态 for i in range(child_count): child = parent_item.child(i) state = child.checkState(0) if state == Qt.Checked: checked_count += 1 elif state == Qt.Unchecked: unchecked_count += 1 # 更新父节点状态 if checked_count == child_count: parent_item.setCheckState(0, Qt.Checked) # 全选 elif unchecked_count == child_count: parent_item.setCheckState(0, Qt.Unchecked) # 未选 else: parent_item.setCheckState(0, Qt.PartiallyChecked) # 半选 # 递归更新上层父节点 self._update_parent_check_state(parent_item.parent()) # 以下为原有右键菜单等功能(无修改) def show_right_menu(self, pos): current_item = self.tree_widget.itemAt(pos) if not current_item: return menu = QMenu(self.tree_widget) info_action = QAction("查看节点信息", self) info_action.triggered.connect(lambda: self.show_item_info(current_item)) menu.addAction(info_action) toggle_action = QAction("勾选/取消勾选", self) toggle_action.triggered.connect(lambda: self.toggle_item_check(current_item)) menu.addAction(toggle_action) menu.addSeparator() del_action = QAction("删除节点", self) del_action.triggered.connect(lambda: self.delete_item(current_item)) menu.addAction(del_action) menu.exec_(self.tree_widget.mapToGlobal(pos)) def show_item_info(self, item): state_map = { Qt.Unchecked: "未勾选", Qt.PartiallyChecked: "半选", Qt.Checked: "已勾选" } check_state = state_map.get(item.checkState(0), "未知") QMessageBox.information( self, "节点信息", f"名称:{item.text(0)}\n状态:{check_state}" ) def toggle_item_check(self, item): current_state = item.checkState(0) new_state = Qt.Unchecked if current_state == Qt.Checked else Qt.Checked item.setCheckState(0, new_state) def delete_item(self, item): if item.parent() is None: index = self.tree_widget.indexOfTopLevelItem(item) self.tree_widget.takeTopLevelItem(index) else: item.parent().removeChild(item) if __name__ == "__main__": app = QApplication(sys.argv) window = TreeWidgetDemo() window.show() sys.exit(app.exec_())

2.TreeView实现树形列表 勾选与右键菜单

import sys from PyQt5.QtWidgets import ( QApplication, QMainWindow, QTreeView, QMenu, QAction, QMessageBox, QAbstractItemView ) from PyQt5.QtCore import Qt, QModelIndex from PyQt5.QtGui import QStandardItemModel, QStandardItem class TreeViewDemo(QMainWindow): def __init__(self): super().__init__() self._is_updating = False # 防递归死循环标志 self.init_ui() def init_ui(self): self.setWindowTitle("QTreeView 勾选联动示例") self.setGeometry(100, 100, 600, 400) # 1. 创建模型 self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(["文件目录"]) # 2. 创建 TreeView self.tree_view = QTreeView(self) self.setCentralWidget(self.tree_view) self.tree_view.setModel(self.model) self.tree_view.setSelectionMode(QAbstractItemView.ExtendedSelection) self.tree_view.setExpandsOnDoubleClick(True) self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view.customContextMenuRequested.connect(self.show_right_menu) # 3. 构建模型节点 self._create_model_items() # 4. 监听模型数据变化 self.model.dataChanged.connect(self.on_model_data_changed) self.tree_view.expandAll() def _create_model_items(self): """创建带勾选联动的节点""" # 根节点1:文档 root1 = QStandardItem("文档") root1.setCheckable(True) root1.setCheckState(Qt.Unchecked) self.model.appendRow(root1) # 子节点1-1/1-2 child1_1 = QStandardItem("简历.docx") child1_1.setCheckable(True) child1_1.setCheckState(Qt.Unchecked) root1.appendRow(child1_1) child1_2 = QStandardItem("报告.pdf") child1_2.setCheckable(True) child1_2.setCheckState(Qt.Unchecked) root1.appendRow(child1_2) # 根节点2:图片(多级示例) root2 = QStandardItem("图片") root2.setCheckable(True) root2.setCheckState(Qt.Unchecked) self.model.appendRow(root2) # 二级节点:风景 child2_1 = QStandardItem("风景") child2_1.setCheckable(True) child2_1.setCheckState(Qt.Unchecked) root2.appendRow(child2_1) # 三级节点:山川/湖泊 child2_1_1 = QStandardItem("山川.jpg") child2_1_1.setCheckable(True) child2_1_1.setCheckState(Qt.Unchecked) child2_1.appendRow(child2_1_1) child2_1_2 = QStandardItem("湖泊.jpg") child2_1_2.setCheckable(True) child2_1_2.setCheckState(Qt.Unchecked) child2_1.appendRow(child2_1_2) def on_model_data_changed(self, top_left: QModelIndex, bottom_right: QModelIndex): """模型数据变化(勾选状态)联动处理""" if self._is_updating or top_left.column() != 0: # 防递归/只处理第0列 return self._is_updating = True try: item = self.model.itemFromIndex(top_left) current_state = item.checkState() # 1. 父节点变化:同步所有子节点 self._set_child_check_state(item, current_state) # 2. 子节点变化:更新所有父节点 self._update_parent_check_state(item.parent()) finally: self._is_updating = False def _set_child_check_state(self, parent_item: QStandardItem, check_state): """递归设置子节点勾选状态""" row_count = parent_item.rowCount() for i in range(row_count): child = parent_item.child(i) if child.isCheckable(): child.setCheckState(check_state) self._set_child_check_state(child, check_state) # 递归处理孙子节点 def _update_parent_check_state(self, parent_item: QStandardItem): """递归更新父节点状态(全选/半选/未选)""" if not parent_item: # 无父节点则终止 return row_count = parent_item.rowCount() checked_count = 0 unchecked_count = 0 # 统计子节点状态 for i in range(row_count): child = parent_item.child(i) if child.isCheckable(): state = child.checkState() if state == Qt.Checked: checked_count += 1 elif state == Qt.Unchecked: unchecked_count += 1 # 更新父节点状态 if checked_count == row_count: parent_item.setCheckState(Qt.Checked) elif unchecked_count == row_count: parent_item.setCheckState(Qt.Unchecked) else: parent_item.setCheckState(Qt.PartiallyChecked) # 递归更新上层父节点 self._update_parent_check_state(parent_item.parent()) # 以下为原有右键菜单等功能(无修改) def show_right_menu(self, pos): index = self.tree_view.indexAt(pos) if not index.isValid(): return menu = QMenu(self.tree_view) info_action = QAction("查看节点信息", self) info_action.triggered.connect(lambda: self.show_item_info(index)) menu.addAction(info_action) toggle_action = QAction("勾选/取消勾选", self) toggle_action.triggered.connect(lambda: self.toggle_item_check(index)) menu.addAction(toggle_action) menu.addSeparator() del_action = QAction("删除节点", self) del_action.triggered.connect(lambda: self.delete_item(index)) menu.addAction(del_action) menu.exec_(self.tree_view.mapToGlobal(pos)) def show_item_info(self, index): item = self.model.itemFromIndex(index) state_map = { Qt.Unchecked: "未勾选", Qt.PartiallyChecked: "半选", Qt.Checked: "已勾选" } check_state = state_map.get(item.checkState(), "未知") QMessageBox.information( self, "节点信息", f"名称:{item.text()}\n状态:{check_state}" ) def toggle_item_check(self, index): item = self.model.itemFromIndex(index) current_state = item.checkState() new_state = Qt.Unchecked if current_state == Qt.Checked else Qt.Checked item.setCheckState(new_state) def delete_item(self, index): if not index.parent().isValid(): self.model.removeRow(index.row()) else: self.model.removeRow(index.row(), index.parent()) if __name__ == "__main__": app = QApplication(sys.argv) window = TreeViewDemo() window.show() sys.exit(app.exec_())

相关文档

PyQt5列表介绍【树控件】-QTreeWidget

https://zhuanlan.zhihu.com/p/17204967078https://zhuanlan.zhihu.com/p/17204967078

PyQt5列表介绍【树控件】-QTreeView

https://zhuanlan.zhihu.com/p/17439921032https://zhuanlan.zhihu.com/p/17439921032

http://www.cnnetsun.cn/news/119705.html

相关文章:

  • 10分钟变身LOL大神:LeaguePrank身份伪装完整指南
  • 5分钟掌握LOL游戏形象定制:LeaguePrank合规美化工具使用指南
  • ConnectivityFilter数据集中分离的区域或连通分量
  • AI 编程的“90% 陷阱”:为什么你生成代码 1 分钟,修 Bug 却要 1 小时?
  • 终极免费抽奖神器:Magpie-LuckyDraw全平台部署指南
  • 技术人才职业发展:从工具思维到价值创造的成长阶梯
  • 百度贴吧用户脚本终极指南:告别繁琐操作,体验贴吧新境界
  • 等待节点-–-behaviac
  • Nginx性能优化实战:从基础配置到高级调优的完整指南
  • ThingsGateway:开源智能设备管理平台的终极指南
  • KolodaView开源项目贡献指南
  • 5‘-Thiol Modifier C6 S-S Amidite,5‘-硫醇修饰剂 C6 双硫键核苷酸酰胺化试剂
  • Python:SOLID 面向对象设计原则
  • 专业级鼠标性能测试工具:从数据采集到精准分析的全链路解析
  • Magpie-LuckyDraw:5分钟上手的多平台炫酷抽奖系统终极指南
  • 魔兽争霸III现代化修复工具:全面解决兼容性问题的终极指南
  • 数字内容获取革命:智能绕过付费墙的完整解决方案
  • 256台H100服务器算力中心的带外管理网络建设方案
  • 深入理解指针(7)
  • 从卷 Java 到冲网安!计算机人 2025 自救路线:附 40-150 万安全岗 + 技能衔接清单
  • python大数据的基于k-means算法的校园美食推荐系统_j4eg7g7z--论文
  • MouseTester专业指南:3步完成鼠标性能精准诊断
  • [鸿蒙2025领航者闯关]图标资源统一管理
  • 区分__proto__和prototype
  • 西门子PLC地址知识点
  • EmotiVoice开源项目依赖项管理最佳实践
  • 如何彻底解决腾讯游戏卡顿问题:sguard_limit资源限制器完整指南
  • MiniGPT-4终极优化指南:5个简单技巧实现3倍推理加速
  • 鼠标性能测试终极指南:从新手到专家的完整解决方案
  • 终极指南:如何用pbxproj轻松玩转Xcode项目文件