【问题标题】:How can I efficiently expand an entire subtree of a QTreeView?如何有效地扩展 QTreeView 的整个子树?
【发布时间】:2011-06-27 08:07:21
【问题描述】:

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

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

MacOS 构建的 Qt 可能有一种方法让用户扩展整个 QTreeView 子树(它有一个开放的错误),但非 MacOS 构建绝对没有。我正在尝试实现“在项目装饰上的 shift-click 展开整个子树”的行为。

有两个问题。两者中较容易的是检测装饰上的 shift-click。我通过拦截展开/折叠的信号来做到这一点;他们检查 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() 非常快。

一些资料显示,expandAll() 的工作量明显少于 expand()。但是,Qt API 没有公开 expandSubtree() 方法。在不深入研究私有实现的情况下,如何快速实现这一点?

【问题讨论】:

    标签: qt4 pyqt qtreeview


    【解决方案1】:

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

    【讨论】:

    • 是的;主要缺点是在将当前更改为所需索引、发送事件和更改当前时需要发出 currentChanged()。
    【解决方案2】:

    由于expandAll()快,collapse(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)
    

    【讨论】:

      【解决方案3】:
      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);
        }
      }
      

      【讨论】:

        【解决方案4】:

        由于我在尝试扩展子树时遇到了性能问题,而且这个问题是谷歌的第一个结果,我将分享我的见解,尽管我使用的是 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);
        }
        

        【讨论】:

          猜你喜欢
          • 2014-09-06
          • 2018-05-15
          • 2015-12-14
          • 2019-08-13
          • 1970-01-01
          • 2016-04-24
          • 1970-01-01
          • 2020-03-11
          • 1970-01-01
          相关资源
          最近更新 更多