【问题标题】:Expand specific items in a treeview during filtering在过滤期间展开树视图中的特定项目
【发布时间】:2019-06-26 21:31:05
【问题描述】:

让我们考虑一下这个树数据:

Root
|-v A1
| |-- textA
|
|-v B1
| |-- textB

在搜索“A1”时,我希望 A1 项目不展开(但可以展开以查看子项):

Root
|-> A1

搜索“textA”时,我希望展开 A1 项(以查看匹配的子项):

Root
|-v A1
| |-- textA

QSortFilterProxyModel 提供的(标准)过滤工作正常,但我不知道如何实现“在需要时扩展”的东西。

我尝试了一些愚蠢的代码,但没有成功:

bool MySortFilterProxyModel::filterAcceptsRow(
  int sourceRow,
  const QModelIndex &sourceParent
) const {
  bool result = QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);

  if (m_tree) {
  //   if (result)
       m_tree->expand(sourceParent);
  }
 return result;
}

【问题讨论】:

    标签: c++ qt qt5 qtreeview qsortfilterproxymodel


    【解决方案1】:

    我认为,以下解决方案可能会对您有所帮助。

    我认为这是扩展项目的一种明智的方式,如果它们只是几个具有被搜索名称的孩子。如果更改搜索字段,则未解决的问题可能是子项的折叠。在搜索字段中输入例如 C-0-0-0 以了解我的解决方案的行为。

    #include <QApplication>
    #include <QTreeView>
    #include <QDebug>
    #include <QVBoxLayout>
    #include <QLineEdit>
    #include <QPushButton>
    #include <QStandardItemModel>
    #include <QSortFilterProxyModel>
    
    
    int main(int argc, char **args)
    {
        QApplication app(argc, args);
        auto frame = new QFrame;
        frame->setLayout(new QVBoxLayout);
        auto model = new QStandardItemModel;
        for (auto i=0; i<10; i++)
        {
            auto item = new QStandardItem(QString("A-%1").arg(i));
            model->appendRow(item);
            for (auto j=0; j<10; j++)
            {
                auto item2 = new QStandardItem(QString("B-%1-%2").arg(i).arg(j));
                item->appendRow(item2);
                for (auto k=0; k<10; k++)
                {
                    auto item3 = new QStandardItem(QString("C-%1-%2-%3").arg(i).arg(j).arg(k));
                    item2->appendRow(item3);
                }
            }
        }
    
        auto proxyModel = new QSortFilterProxyModel;
        proxyModel->setSourceModel(model);
        proxyModel->setRecursiveFilteringEnabled(true);
        auto view = new QTreeView;
        view->setModel(proxyModel);
        frame->layout()->addWidget(view);
        auto edit = new QLineEdit;
        frame->layout()->addWidget(edit);
        frame->show();
        QObject::connect(edit, &QLineEdit::textChanged, [&](auto test) {
            proxyModel->setFilterFixedString(test);
            Qt::MatchFlags flags;
            flags.setFlag(Qt::MatchFlag::MatchStartsWith, true);
            flags.setFlag(Qt::MatchFlag::MatchWrap, true);
            flags.setFlag(Qt::MatchFlag::MatchRecursive, true);
            auto indexList=proxyModel->match(proxyModel->index(0,0), Qt::ItemDataRole::DisplayRole, test, -1, flags);
            for (auto index : indexList)
            {
                auto expanderIndex = index.parent();
                while (expanderIndex.isValid())
                {
                    view->expand(expanderIndex);
                    expanderIndex = expanderIndex.parent();
                }
    
            }
            qDebug() << indexList.size();
        });
        app.exec();
    }
    

    改进的解决方案

    以下改进的解决方案仅使用模型找到的索引,而不使用match 提供的可能不同的索引搜索。

    ma​​in.cpp

    #include <QApplication>
    #include <QTreeView>
    #include <QDebug>
    #include <QVBoxLayout>
    #include <QLineEdit>
    #include <QPushButton>
    #include <QStandardItemModel>
    #include <QSortFilterProxyModel>
    #include "MyProxyModel.h"
    
    int main(int argc, char **args)
    {
        QApplication app(argc, args);
        auto frame = new QFrame;
        frame->setLayout(new QVBoxLayout);
        auto model = new QStandardItemModel;
        for (auto i=0; i<10; i++)
        {
            auto item = new QStandardItem(QString("A-%1").arg(i));
            model->appendRow(item);
            for (auto j=0; j<10; j++)
            {
                auto item2 = new QStandardItem(QString("B-%1-%2").arg(i).arg(j));
                item->appendRow(item2);
                for (auto k=0; k<10; k++)
                {
                    auto item3 = new QStandardItem(QString("C-%1-%2-%3").arg(i).arg(j).arg(k));
                    item2->appendRow(item3);
                }
            }
        }
    
        auto proxyModel = new MyProxyModel;
        proxyModel->setSourceModel(model);
        proxyModel->setRecursiveFilteringEnabled(true);
        auto view = new QTreeView;
        view->setModel(proxyModel);
        frame->layout()->addWidget(view);
        auto edit = new QLineEdit;
        frame->layout()->addWidget(edit);
        frame->show();
        QObject::connect(edit, &QLineEdit::textChanged, [&](auto test) {
            proxyModel->setFilterFixedString(test);
            if (test == "") return;
            view->collapseAll();
            QList<QModelIndex> acceptedIndices = proxyModel->findIndices();
            for (auto index : acceptedIndices)
            {
                auto expanderIndex = index.parent();
                while (expanderIndex.isValid())
                {
                    view->expand(expanderIndex);
                    expanderIndex = expanderIndex.parent();
                }
    
            }
            qDebug() << acceptedIndices.size();
        });
        app.exec();
    }
    

    MyProxyModel.h

    #pragma once
    
    #include <QSortFilterProxyModel>
    
    class MyProxyModel : public QSortFilterProxyModel
    {
        Q_OBJECT
    public:
        QList<QModelIndex> findIndices() const
        {
            QList<QModelIndex> ret;
            for (auto iter=0; iter < rowCount(); iter++)
            {
                auto childIndex = index(iter, 0, QModelIndex());
                ret << recursivelyFindIndices(childIndex);
            }
            return ret;
        }
    
    
        bool rowAccepted(int source_row, const QModelIndex& source_parent) const
        {
            return filterAcceptsRow(source_row, source_parent);
        }
    private:
        QList<QModelIndex> recursivelyFindIndices(const QModelIndex& ind) const
        {
            QList<QModelIndex> ret;
            if (rowAccepted(ind.row(), ind.parent()))
            {
                ret << ind;
            }
            for (auto iter=0; iter<rowCount(ind); iter++)
            {
                ret << recursivelyFindIndices(index(iter, 0, ind));
            }
            return ret;
        }
    };
    

    【讨论】:

    • 嗯,这有点工作,但你在这里强制进行第二次搜索/匹配,并且使用与第一次过滤不同的选项。我想知道这里的性能问题。
    • @Salamandar:另一种选择可能是连接到proxyModels 的信号。如果模型发生变化,可以通过外部调用filterAcceptRows 获取所有当前索引,可以单独检查它们是否匹配。
    • @Salamandar:我改进了我的解决方案。也许这应该符合您的需求。
    • 我不得不对其进行一些修改,但它可以正常工作!我在 recursivelyFindIndices 中添加了一个“else”,仅在节点不匹配时才进行递归。
    • @Salamandar:很高兴听到。通过解决这个问题,我也学到了很多东西。
    【解决方案2】:

    只是对@Aleph0 回答的一些建议:

    1. ma​​in.cpp 片段:
            view->collapseAll();
            QList<QModelIndex> acceptedIndices = proxyModel->findIndices();
            for (auto index : acceptedIndices)
            {
                auto expanderIndex = index.parent();
                while (expanderIndex.isValid())
                {
                    view->expand(expanderIndex);
                    expanderIndex = expanderIndex.parent();
                }
    
            }
    

    为了防止重复扩展,最好检查 expanderIndex 是否已经扩展并停止上升。在最坏的情况下,它将使您免于 O(n^2) 复杂性(应该是线性的)。

    1. 以类似的方式,MyProxyModel::findIndices 方法可以在没有递归的情况下实现以加快处理速度(但是,现代编译器应该足够聪明以展开它)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-02
      • 2013-02-22
      • 1970-01-01
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 2016-07-16
      相关资源
      最近更新 更多