如何有效扩展QTreeView的整个子树?

时间:2011-02-15 03:06:08

标签: qt4 pyqt qtreeview

编辑:事实证明,root性能问题是一个附加到expanded()信号的大小适合函数,因此我将接受第一个答案并删除这个问题,因为它具有误导性。

注意:我问这个问题所以我可以为它提供答案(也许可以得到更好的答案)。解决方案不直观。

Qt的MacOS版本可能为用户提供了扩展整个QTreeView子树的方法(有一个开放的bug),但非MacOS版本肯定不会。我正在尝试实现“按住项目装饰的移动点击扩展整个子树”的行为。

有两个问题。两者中较容易的是检测到装饰上的移位点击。我通过截取扩展/折叠信号来做到这一点;他们检查了mousePressEvent设置的一些“全局”状态:

# similar implementation for _on_expanded
@pyqtSlot(QModelIndex)
def _on_collapsed(self, index):
    if self._in_shift_press:
        self._in_shift_press = False
        _recursive_set_expanded(self, index, False)

def mousePressEvent(self, evt):
    # Make shift-click expand/collapse all
    if int(evt.modifiers() & Qt.ShiftModifier) != 0:
        self._in_shift_press = True
    try:
        QTreeView.mousePressEvent(self, evt)
    finally:
        self._in_shift_press = False

这有点难看,但效果还不错。

更难的问题是实现_recursive_set_expanded(view,root,state)。递归崩溃非常快。但是,在索引的所有后代上调用view.setExpanded(True)的明显实现非常非常慢 - 约100个索引的多秒。问题不是昂贵的数据模型,因为view.expandAll()非常快。

一些消息来源显示,expand()的工作量远远少于expand()。但是,Qt API不公开expandSubtree()方法。如何快速实现这一目标,而不是深入挖掘私有实施?

4 个答案:

答案 0 :(得分:4)

按'*'(星号)键可展开节点的子节点,这正是您想要的。您是否尝试使用假'*'按键调用keyPressEvent?

答案 1 :(得分:1)

由于expandAll()很快,而且崩溃(QModelIndex)很快,我的解决方案是只使用这两种方法并避免调用expand(QModelIndex):

def _recursive_set_expanded(view, root, desired):
    """For |root| and all its descendents, set the 'expanded' state to |desired|.
    It is undefined whether expanded and collapsed signals are emitted."""
    state = {}
    def _recursive_get_state(view, model, index, under_root):
        if index == root:
            under_root = True
        num_children = model.rowCount(index)
        if num_children:
            if under_root:
                state[index] = desired
            else:
                state[index] = view.isExpanded(index)
            for i in xrange(model.rowCount(index)):
                _recursive_get_state(view, model, model.index(i,0, index), under_root)

    _recursive_get_state(view, view.model(), QModelIndex(), False)
    view.expandAll()
    for k,v in state.iteritems():
        if not v:
            view.setExpanded(k, False)

答案 2 :(得分:0)

void MainWindow::expandNode(const QModelIndex &parentIndex, bool expand) {
  tree->setExpanded(parentIndex, expand);
  for (qint32 rowNum = 0; rowNum < treeModel->rowCount(parentIndex); ++rowNum) {
    QModelIndex childIndex = treeModel->index(rowNum, 0, parentIndex);
    tree->setExpanded(childIndex, expand);
    expandNode(childIndex);
  }
}

答案 3 :(得分:0)

由于我在尝试扩展子树时遇到了性能问题,而这个问题是第一个google结果,我将分享我的见解,尽管我使用的是C ++和Qt5。

TonyK的调用keyPressEvent的解决方案有效。但是,这个解决方案对我来说很笨拙,所以我检查了Qt源代码,它确实硬连接到Qt :: Key_Asterisk密钥。 (在QTreeView :: keyPressEvent中,如果你想亲眼看看。)

QTreeView :: keyPressEvent只是以深度优先的方式调用expand(),没有什么特别的,没有私有的api调用。当然,当我刚从keyPressEvent复制代码以便我的功能时,它起作用了:

QModelIndex current = currentIndex();
QStack<QModelIndex> parents;
parents.push(current);
QAbstractItemModel * mod = model();
while (!parents.isEmpty()) {
    QModelIndex parent = parents.pop();
    for (int row = 0; row < mod->rowCount(parent); ++row) {
        QModelIndex child = mod->index(row, 0, parent);
        if (!child.isValid())
            break;
        parents.push(child);
        expand(child);
    }
}
expand(current);

但是我想创建一个setExpandedRecursive(QModelIndex index, bool expanded)函数,所以我检查了它们与我的代码的区别,并且只是他们为子树的根节点调用expand()而不是第一个。所以我在我的功能中也这样做了,猜猜是什么,现在崩溃是问题而不是扩展。所以这是我的最终解决方案,适用于扩展折叠子树:

void TreeViewClass::setExpandedRecursive(QModelIndex index, bool expanded)
{
    if (!index.isValid())
        return;

    const QAbstractItemModel * model = index.model();
    if (!expanded)
        setExpanded(index, expanded);
    for (int row = 0, count = model->rowCount(index); row < count; ++row)
        setExpandedRecursive(model->index(row, 0, index), expanded);
    if (expanded)
        setExpanded(index, expanded);
}