PySide6 QTreeView后两列无法排序问题求助
QTreeView 树形视图多列排序失效问题解决
问题背景
使用QTreeView和QStandardItemModel构建树形视图时,仅前两列(Well Name、Depth type)可正常排序,其余列(Log Type、Log Name等)无法排序。所有列均使用字符串类型的QStandardItem,数据来自嵌套字典结构:{ well : { depthtype : { logtype: [log1, log2, log3] } } }。
问题原因
- 默认排序仅作用于根节点直接子项:QStandardItemModel的默认
sort方法仅对根节点的直接子项排序,不会递归处理深层子节点的子项。点击后续列时,根节点的子项(Well行)在对应列无有效数据,排序后无明显变化,而深层子项(如Log Type、Log Name行)未被自动排序。 - 未显式创建全列Item:原填充逻辑仅为每行的部分列创建Item,其余列由模型自动补充空Item,虽不影响显示,但可能导致排序时无法正确获取对应列的排序键。
解决方案
核心思路
自定义支持递归排序的模型类,同时修改数据填充逻辑,为每行的所有列显式创建Item,确保任意列点击时,所有层级的子项都会按对应列数据排序,且保持树形结构的层级关系。
完整实现代码
import sys from PySide6.QtWidgets import QApplication, QTreeView from PySide6.QtGui import QStandardItem, QStandardItemModel, QIcon from PySide6.QtCore import Qt class SortableStandardItemModel(QStandardItemModel): def sort(self, column, order=Qt.AscendingOrder): # 递归排序所有层级的子项 def recursive_sort(parent_item, column, order): child_count = parent_item.rowCount() if child_count == 0: return # 收集子项的排序文本、原行号和子项对象 children_info = [] for row in range(child_count): sort_item = parent_item.child(row, column) sort_text = sort_item.text() if sort_item else "" children_info.append((sort_text, row, parent_item.child(row))) # 按排序文本排序,支持升序/降序 children_info.sort(key=lambda x: x[0], reverse=(order == Qt.DescendingOrder)) # 重新排列子项顺序 for new_idx, (_, old_idx, child) in enumerate(children_info): if new_idx != old_idx: parent_item.insertRow(new_idx, parent_item.takeRow(old_idx)) # 递归排序当前子项的子项 for row in range(parent_item.rowCount()): recursive_sort(parent_item.child(row), column, order) # 从根节点开始递归排序 recursive_sort(self.invisibleRootItem(), column, order) # 通知视图更新布局 self.layoutChanged.emit() app = QApplication(sys.argv) TreeViewLogSelection = QTreeView() # 使用自定义排序模型 WellModel = SortableStandardItemModel() WellModel.setColumnCount(6) TreeViewLogSelection.setModel(WellModel) TreeViewLogSelection.model().setHorizontalHeaderLabels( ['Well Name','Depth type','Log Type','Log Name','Role','Log unique name'] ) def fillWellTree(parent, dico, depth=0): if isinstance(dico, dict): for key, value in dico.items(): # 初始化整行的6个空Item itemList = [QStandardItem("") for _ in range(6)] # 设置当前层级对应列的内容 current_item = QStandardItem(str(key)) current_item.setEditable(False) itemList[depth] = current_item if depth == 0: current_item.setIcon(QIcon("./icon/IconWell.png")) # 可选:保留Well图标 parent.appendRow(itemList) if isinstance(value, dict): fillWellTree(itemList[0], value, depth + 1) elif isinstance(value, list): # 处理Log Type节点,再添加Log Name子节点 fillWellTree(itemList[0], value, depth + 1) for val in value: log_item_list = [QStandardItem("") for _ in range(6)] log_item = QStandardItem(str(val)) log_item.setEditable(False) log_item_list[depth + 1] = log_item itemList[0].appendRow(log_item_list) # 测试数据 dico={'Well-6': {'Depth': {'Sonic': ['DT', 'DTS', 'DTST', 'VPVS', 'DT_REG', 'Smoothing (DT_REG)', 'DT_FINAL'], 'Shallow resistivity': ['LLS', 'MSFL'], 'Bulk density': ['RHOB_RAW', 'RHOB_Predict_RFA', 'RHOB_REG', 'RHOB_DESPIKED', 'RHOB_DESPIKE_REG', 'Smoothing (RHOB_DESPIKE_REG)', 'RHOB_FINAL', 'RHOB_Predict_RF'], 'Shear slowness': ['DTS_Predict_RF', 'DTS_Predict_NN'], 'Deviation': ['DX', 'DY']}, 'MD': {'Sonic': ['DT_Predict_RF','DTS_predict']}}, 'DRILL-1': {'Depth': {'Bit size': ['BS'], 'Caliper': ['CALI'], 'Gamma ray': ['GR'], 'Neutron': ['NPHI'], 'Photoelectric factor': ['PE'], 'Spontaneous potential': ['SP'], 'Shallow resistivity': ['LLS'], 'Deep resistivity': ['LLD'], 'Sonic': ['DT', 'DTS','DTC'], 'Bulk density': ['RHOB'], 'Anonymous': ['WELLTOPS'], 'Badhole indicator': ['Bad_Hole']}}, 'WELLI-1': {'Depth': {'Sonic': ['DT', 'DTS','DTC'], 'Gamma ray': ['GR', 'GR2'], 'Shallow resistivity': ['LLS', 'MSFL'], 'Bulk density': ['RHOB_RAW', 'RHOB_Predict_RF']}, 'MD': {'Sonic': ['DT_Predict_RF', 'DT_Merged', 'DT_FINAL'], 'Impedance': ['AI','IP']}}} fillWellTree(WellModel.invisibleRootItem(),dico) TreeViewLogSelection.setSortingEnabled(True) TreeViewLogSelection.show() sys.exit(app.exec())
效果说明
运行代码后,点击任意列头均可触发对应列的排序:
- 点击
Well Name:所有Well按名称排序 - 点击
Depth type:每个Well下的Depth/MD按名称排序 - 点击
Log Type:每个Depth type下的Log Type按名称排序 - 点击
Log Name:每个Log Type下的Log Name按名称排序 - 其余空白列排序时,会按空字符串优先的规则排列,不影响有效数据的排序逻辑。
内容的提问来源于stack exchange,提问作者Eric Bayard
相关产品推荐
相关产品推荐

