2

我希望能够在 QTreeView 中展开或折叠特定分支的所有子项。我正在使用 PyQt4。

我知道 QTreeView 有一个绑定到 * 的扩展所有子项功能,但我需要两件事:它需要绑定到不同的组合键(shift-space),我还需要能够折叠所有子项.

这是我到目前为止所尝试的:我有一个 QTreeView 的子类,其中我正在检查 shift-space 键组合。我知道 QModelIndex 会让我用“child”功能选择一个特定的孩子,但这需要知道孩子的数量。我可以通过查看 internalPointer 来计算孩子的数量,但这只能为我提供层次结构第一级的信息如果我尝试使用递归,我可以获得一堆子计数,但是我不知道如何将这些转换回有效的 QModelIndex。

这是一些代码:

def keyPressEvent(self, event):
    """
    Capture key press events to handle:
    - enable/disable
    """
    #shift - space means toggle expanded/collapsed for all children
    if (event.key() == QtCore.Qt.Key_Space and 
        event.modifiers() & QtCore.Qt.ShiftModifier):
        expanded = self.isExpanded(self.selectedIndexes()[0])
        for cellIndex in self.selectedIndexes():
            if cellIndex.column() == 0: #only need to call it once per row
                #I can get the actual object represented here
                item = cellIndex.internalPointer()
                #and I can get the number of children from that
                numChildren = item.get_child_count()
                #but now what? How do I convert this number into valid
                #QModelIndex objects? I know I could use: 
                #   cellIndex.child(row, 0)
                #to get the immediate children's QModelIndex's, but how
                #would I deal with grandchildren, great grandchildren, etc...
                self.setExpanded(cellIndex, not(expanded))
        return

这是我正在调查的递归方法的开始,但是在实际尝试设置展开状态时我遇到了困难,因为一旦进入递归,我就失去了与任何有效 QModelIndex 的“联系”......

def toggle_expanded(self, item, expand):
    """
    Toggles the children of item (recursively)
    """
    for row in range(0,item.get_child_count()):
        newItem = item.get_child_at_row(row)
        self.toggle_expanded(newItem, expand)
    #well... I'm stuck here because I'd like to toggle the expanded
    #setting of the "current" item, but I don't know how to convert
    #my pointer to the object represented in the tree view back into
    #a valid QModelIndex
    #self.setExpanded(?????, expand)   #<- What I'd like to run
    print "Setting", item.get_name(), "to", str(expand) #<- simple debug statement that indicates that the concept is valid

感谢大家花时间看这个!

4

4 回答 4

4

好吧...兄弟姐妹实际上并没有把我带到我想去的地方。我设法使代码按如下方式工作(这似乎是一个不错的实现)。仍然要感谢 Ebral 教授,他让我带着兄弟姐妹的想法走上了正确的轨道(结果我需要使用 QModelIndex.child(row, column) 并从那里递归迭代)。

请注意,代码中有以下假设:它假设您的基础数据存储对象能够报告他们有多少孩子(我的代码中的 get_child_count() )。如果不是这种情况,您将不得不以不同的方式获得子项计数......也许只是通过任意尝试获取子索引 - 使用 QModelIndex.child(row, col) - 行数不断增加,直到你回来无效索引?- 这是Prof.Ebral建议的,我可能仍然会尝试(只是我已经有一种简单的方法可以通过从我的数据存储中请求来获取孩子的数量)。

另请注意,我实际上是根据我是展开还是折叠,在递归的不同点展开/折叠每个节点。这是因为,通过反复试验,我发现如果我只是在代码中的一个地方做动画树视图会卡顿和弹出。现在,通过根据我是否处于顶层(即我正在影响的分支的根 - 而不是整个树视图的根)来反转我执行此操作的顺序,我得到了一个很好的平滑动画。这在下面记录。

以下代码位于 QTreeView 子类中。

#---------------------------------------------------------------------------
def keyPressEvent(self, event):

    if (event.key() == QtCore.Qt.Key_Space and self.currentIndex().column() == 0):
        shift = event.modifiers() & QtCore.Qt.ShiftModifier
        if shift:
            self.expand_all(self.currentIndex())
        else:                
            expand = not(self.isExpanded(self.currentIndex()))
            self.setExpanded(self.currentIndex(), expand)


#---------------------------------------------------------------------------
def expand_all(self, index):
    """
    Expands/collapses all the children and grandchildren etc. of index.
    """
    expand = not(self.isExpanded(index))
    if not expand: #if collapsing, do that first (wonky animation otherwise)
        self.setExpanded(index, expand)    
    childCount = index.internalPointer().get_child_count()
    self.recursive_expand(index, childCount, expand)
    if expand: #if expanding, do that last (wonky animation otherwise)
        self.setExpanded(index, expand)


#---------------------------------------------------------------------------
def recursive_expand(self, index, childCount, expand):
    """
    Recursively expands/collpases all the children of index.
    """
    for childNo in range(0, childCount):
        childIndex = index.child(childNo, 0)
        if expand: #if expanding, do that first (wonky animation otherwise)
            self.setExpanded(childIndex, expand)
        subChildCount = childIndex.internalPointer().get_child_count()
        if subChildCount > 0:
            self.recursive_expand(childIndex, subChildCount, expand)
        if not expand: #if collapsing, do it last (wonky animation otherwise)
            self.setExpanded(childIndex, expand)
于 2010-11-17T19:20:30.560 回答
2

model.rowCount(index) 是您想要的方法。

model = index.model()   # or some other way of getting it
for i in xrange(model.rowCount(index)):
  child = model.index(i,0, index)
  # do something with child

model.index(row,col, parent) 本质上与调用 index.child(row,col); 相同。只是用更少的间接。

于 2011-02-15T02:05:18.893 回答
1

我建议使用继承 QTreeView 的 QTreeWidget。然后,您可以将子项作为 QTreeWidgetItem 获取。

由于您不想使用 QTreeWidget 但想坚持使用当前模型..您可以使用 .isValid() 遍历“可能的”子级。不过,您不应该使用 internalPointer() 。而是使用您拥有的 cellItem,因为它是原始的 ModalIndex .. 然后尝试找到它的兄弟姐妹。就像是

x = 0; y =0
while cellIndex.sibling(x, y).isValid():
    child = cellIndex.sibling(x, y)
    x += 1
于 2010-11-09T05:37:05.670 回答
1

我为此制作了一个 evnetFilter 类。我的特定用例是 shift 单击下拉指示器,然后展开所有子节点或折叠所有子节点,如软件maya大纲。


class MTreeExpandHook(QtCore.QObject):
    """
    MTreeExpandHook( QTreeView )
    """

    def __init__(self, tree):
        super(MTreeExpandHook, self).__init__()
        tree.viewport().installEventFilter(self)
        self.tree = tree

    def eventFilter(self, receiver, event):
        if (
            event.type() == QtCore.QEvent.Type.MouseButtonPress
            and event.modifiers() & QtCore.Qt.ShiftModifier
        ):
            pos = self.tree.mapFromGlobal(QtGui.QCursor.pos())
            index = self.tree.indexAt(pos)
            if not self.tree.isExpanded(index):
                self.tree.expandRecursively(index)
                return True
        return super(MTreeExpandHook, self).eventFilter(self.tree, event)

下面的使用示例


import sys
from PySide2 import QtCore,QtGui,QtWidgets

class MTreeExpandHook(QtCore.QObject):
    """
    MTreeExpandHook( QTreeView )
    """

    def __init__(self, tree):
        super(MTreeExpandHook, self).__init__()
        self.setParent(tree)
        # NOTE viewport for click event listen
        tree.viewport().installEventFilter(self)
        self.tree = tree

    def eventFilter(self, receiver, event):
        if (
            # NOTE mouse left click 
            event.type() == QtCore.QEvent.Type.MouseButtonPress
            # NOTE keyboard shift press
            and event.modifiers() & QtCore.Qt.ShiftModifier
        ):
            # NOTE get mouse local position
            pos = self.tree.mapFromGlobal(QtGui.QCursor.pos())
            index = self.tree.indexAt(pos)
            if not self.tree.isExpanded(index):
                # NOTE expand all child
                self.tree.expandRecursively(index)
                return True
        return super(MTreeExpandHook, self).eventFilter(self.tree, event)
    

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    
    model = QtGui.QStandardItemModel()
    
    # NOTE create nested data
    for i in range(3):
        parent = QtGui.QStandardItem('Family {}'.format(i))
        for j in range(3):
            child = QtGui.QStandardItem('Child {}'.format(i*3+j))
            for k in range(3):
                sub_child = QtGui.QStandardItem("Sub Child")
                child.appendRow([sub_child])
                for x in range(2):
                    sub_child_2 = QtGui.QStandardItem("Sub Child 2")
                    sub_child.appendRow([sub_child_2])
            parent.appendRow([child])
        model.appendRow(parent)

        
    treeView = QtWidgets.QTreeView()
    treeView.setHeaderHidden(True)
    MTreeExpandHook(treeView)
    treeView.setModel(model)
    treeView.show()
    
    sys.exit(app.exec_())

示例 gif

于 2021-07-14T05:05:07.023 回答