You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6 QTreeView后两列无法排序问题求助

QTreeView 树形视图多列排序失效问题解决

问题背景

使用QTreeView和QStandardItemModel构建树形视图时,仅前两列(Well Name、Depth type)可正常排序,其余列(Log Type、Log Name等)无法排序。所有列均使用字符串类型的QStandardItem,数据来自嵌套字典结构:{ well : { depthtype : { logtype: [log1, log2, log3] } } }。

问题原因

  1. 默认排序仅作用于根节点直接子项:QStandardItemModel的默认sort方法仅对根节点的直接子项排序,不会递归处理深层子节点的子项。点击后续列时,根节点的子项(Well行)在对应列无有效数据,排序后无明显变化,而深层子项(如Log Type、Log Name行)未被自动排序。
  2. 未显式创建全列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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:30:55