如何在QTreeView中禁用指定行的展开功能(保留选中权限)
解决QTreeView指定行隐藏展开箭头但保留选中功能的问题
原代码尝试通过修改QStyledItemDelegate的sizeHint来隐藏展开箭头,这种方法无效——因为展开箭头属于QTreeView的分支装饰(branch),并不归item delegate负责绘制,修改item的尺寸不会影响箭头的显示。
下面提供两种可行的解决方案:
方法一:重写QTreeView的drawBranches方法
直接控制箭头的绘制逻辑,指定行不绘制展开箭头:
import sys from PySide2.QtWidgets import QApplication, QMainWindow, QTreeView, QVBoxLayout, QWidget, QAbstractItemView, QSizePolicy from PySide2.QtCore import Qt, QModelIndex, QRect from PySide2.QtGui import QPainter class CustomTreeView(QTreeView): def __init__(self, parent=None): super().__init__(parent) self.target_row = 5 # 要隐藏箭头的行索引 def drawBranches(self, painter: QPainter, rect: QRect, index: QModelIndex) -> None: # 仅当不是目标行时,才绘制展开箭头 if index.row() != self.target_row: super().drawBranches(painter, rect, index) class CustomItemModel(QAbstractItemModel): def __init__(self, data, parent=None): super().__init__(parent) self.data = data self.max_display_rows = 5 def index(self, row, column, parent=QModelIndex()): if parent.isValid() or column != 0: return QModelIndex() return self.createIndex(row, column, row) def parent(self, index): return QModelIndex() def rowCount(self, parent=QModelIndex()): if parent.isValid(): return 0 return len(self.data) def columnCount(self, parent=QModelIndex()): return 1 def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None if role == Qt.DisplayRole: return self.data[index.row()] return None def flags(self, index): # 确保所有行都可选中,目标行也不例外 flags = super().flags(index) flags |= Qt.ItemIsSelectable | Qt.ItemIsEnabled # 如果需要目标行实际不可展开,可去掉ItemIsExpandable;如果只是隐藏箭头保留展开功能,不要改这行 # if index.row() == self.max_display_rows: # flags &= ~Qt.ItemIsExpandable return flags class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Custom TreeView Example") self.setGeometry(100, 100, 400, 300) data = ["Item {}".format(i) for i in range(1, 11)] self.model = CustomItemModel(data) self.treeView = CustomTreeView() self.treeView.setModel(self.model) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) layout = QVBoxLayout() layout.addWidget(self.treeView) central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) def main(): app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) if __name__ == "__main__": main()
方法二:通过模型flags控制(适合目标行无需展开功能的场景)
如果目标行本身不需要展开功能,可以在模型的flags方法中移除Qt.ItemIsExpandable标记,这样箭头会自动隐藏,同时保留选中功能:
# 在CustomItemModel的flags方法中修改: def flags(self, index): flags = super().flags(index) flags |= Qt.ItemIsSelectable | Qt.ItemIsEnabled # 目标行移除可展开标记,箭头会隐藏 if index.row() == self.max_display_rows: flags &= ~Qt.ItemIsExpandable return flags
说明
- 方法一的优势是:即使目标行实际有子项(可展开),也能只隐藏箭头,保留展开功能(点击行仍可展开,只是看不到箭头)。
- 方法二更简洁,但会同时禁用该行的展开功能,适合确定不需要展开的场景。
内容的提问来源于stack exchange,提问作者Ryan Glenn
相关产品推荐
相关产品推荐

