Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSortFilterProxyModel -filterAcceptsRow()- filter parent Node and child Node
Forum Updated to NodeBB v4.3 + New Features

QSortFilterProxyModel -filterAcceptsRow()- filter parent Node and child Node

Scheduled Pinned Locked Moved General and Desktop
28 Posts 8 Posters 30.9k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    ManasQt
    wrote on 14 Jul 2011, 16:53 last edited by
    #1

    Hi,
    How to filter parent Node and Child Node using QSortFilterProxyModel -filterAcceptsRow()?

    Is it necessary to have QTreeView Expanded Already?

    I want to show Parent Nodes only if child nodes have filtered expression.

    1 Reply Last reply
    0
    • D Offline
      D Offline
      dakron
      wrote on 14 Jul 2011, 19:50 last edited by
      #2

      You need to check if the source_parent is valid, if yes then skip that element (because this is child node), if not then go through all element's (parent node) children and check against you expression and accept or not that element based on results.
      I don't know if this is clear enough so let us know when you need more help.

      1 Reply Last reply
      0
      • M Offline
        M Offline
        ManasQt
        wrote on 14 Jul 2011, 20:56 last edited by
        #3

        Hi dakron,
        thanks for the reply.

          Here what ever you said that all be done by filterAcceptsRow() .
        

        For every row it will verify the condition and those have right filter (True ) will be shown on Tree.

        but, here i am not able to show parent nodes when children have the expression which returns True. QTreeView allways shows only the nodes that returns True if they have expression.

        what if child node has the expression and parent won't then how you will display child node with the parent node to distinguish with other nodes in the tree.

        would appreciate if any one could help meto slove this problem,thanks in advance.

        1 Reply Last reply
        0
        • A Offline
          A Offline
          andre
          wrote on 15 Jul 2011, 05:44 last edited by
          #4

          Well, basically, you do it the other way around in that case :-)
          You use filterAcceptsRow to check if any of the child items match, and if so, you make the parent item match as well. It is not too difficult.

          This is a simple implementation I wrote some time ago. It works well for small models, but it could do with some optimizations. I think the implementation should cache which rows match, as the matching itself could become expensive. Of course, that cache needs to be cleared if the model changes.

          This proxy model will accept rows:

          • That match themselves, or
          • That have a parent that matches (on its own), or
          • That have a child that matches.

          That is: if a parent node matches, it will show all its children, even if they themselves do not match, but if a a child node matches, it will show the parent, but not its (non-matching) siblings.

          @
          //leaffilterproxymodel.h
          #ifndef LEAFFILTERPROXYMODEL_H
          #define LEAFFILTERPROXYMODEL_H

          #include <QSortFilterProxyModel>

          class LeafFilterProxyModel : public QSortFilterProxyModel
          {
          Q_OBJECT
          public:
          explicit LeafFilterProxyModel(QObject *parent = 0);

          signals:

          public slots:

          protected:
          bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
          bool filterAcceptsRowItself(int source_row, const QModelIndex &source_parent) const;
          bool hasAcceptedChildren(int source_row, const QModelIndex &source_parent) const;

          };
          #endif // LEAFFILTERPROXYMODEL_H
          @

          @
          //leaffilterproxymodel.cpp
          #include "leaffilterproxymodel.h"
          #include <QtDebug>

          LeafFilterProxyModel::LeafFilterProxyModel(QObject *parent) :
          QSortFilterProxyModel(parent)
          {
          }

          bool LeafFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
          {
          if (filterAcceptsRowItself(source_row, source_parent))
          return true;

          //accept if any of the parents is accepted on it's own merits
          QModelIndex parent = source_parent;
          while (parent.isValid()) {
              if (filterAcceptsRowItself(parent.row(), parent.parent()))
                  return true;
              parent = parent.parent();
          }
          
          //accept if any of the children is accepted on it's own merits
          if (hasAcceptedChildren(source_row, source_parent)) {
              return true;
          }
          
          return false;
          

          }

          bool LeafFilterProxyModel::filterAcceptsRowItself(int source_row, const QModelIndex &source_parent) const
          {
          return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
          }

          bool LeafFilterProxyModel::hasAcceptedChildren(int source_row, const QModelIndex &source_parent) const
          {
          QModelIndex item = sourceModel()->index(source_row,0,source_parent);
          if (!item.isValid()) {
          //qDebug() << "item invalid" << source_parent << source_row;
          return false;
          }

          //check if there are children
          int childCount = item.model()->rowCount(item);
          if (childCount == 0)
              return false;
          
          for (int i = 0; i < childCount; ++i) {
              if (filterAcceptsRowItself(i, item))
                  return true;
              //recursive call -> NOTICE that this is depth-first searching, you're probably better off with breadth first search...
              if (hasAcceptedChildren(i, item))
                  return true;
          }
          
          return false;
          

          }
          @

          1 Reply Last reply
          1
          • M Offline
            M Offline
            ManasQt
            wrote on 15 Jul 2011, 17:05 last edited by
            #5

            Hi Andre,
            Thanks for your solution , you saved my day. It works as you explained.

            But, " if a parent node matches, it will show all its children, even if they themselves do not match. "
            1.Is there a way to avoid this?
            2.Do we need to have QTreeView Expanded using ExpandAll() before we filter ?

            I wonder, how does search/filter works in Eclipse's Object explorer?
            i think Eclipse also uses QSortFilterProxyModel. What do you think? If it is possible can you please elaborate how to achieve it.

            would appreciate if any one could help meto slove this problem,thanks in advance.

            1 Reply Last reply
            0
            • A Offline
              A Offline
              andre
              wrote on 15 Jul 2011, 17:59 last edited by
              #6

              [quote author="ManasQt" date="1310749514"]Hi Andre,
              Thanks for your solution , you saved my day. It works as you explained.

              But, " if a parent node matches, it will show all its children, even if they themselves do not match. "
              1.Is there a way to avoid this?
              [/quote]
              Of course there is, but it was the behaviour I needed, so that's what I made. There is no setting for it, but I guess you could hack that in easily. What you do, is leave out lines 16 to 21 of the cpp file in that case. Those are the lines that make an item match if a parent matches.

              [quote]2.Do we need to have QTreeView Expanded using ExpandAll() before we filter ?[/quote]
              No, that is not needed, though obviously it won't show items that are not expanded. One exception: if your model is lazy-loaded, you may be in trouble... I did not try that scenario.

              [quote]I wonder, how does search/filter works in Eclipse's Object explorer?
              i think Eclipse also uses QSortFilterProxyModel. What do you think? If it is possible can you please elaborate how to achieve it.
              [/quote]
              I have no idea; I don't use eclipse, and I never felt the need to look at its sources. There is nothing stopping you from looking at them to learn from them though.

              1 Reply Last reply
              0
              • M Offline
                M Offline
                ManasQt
                wrote on 15 Jul 2011, 19:13 last edited by
                #7

                Hi Andre,
                * It did work and thank you so much for your excellent solution.
                *
                It was just my guess that Eclipse is developed on Qt and it could be using searching/filtering in the Object Explorer using "QSortFilterProxyModel". for this reason i thought of achieving the same thing.

                bq. One exception: if your model is lazy-loaded, you may be in trouble… I did not try that scenario. eq.

                Do i have to use "QStandardItemModel" to avoid lazy-loading?

                I use QAbstractItemModel. Initially my treeview will show only root node as expanded not childs because treeview contains hundreds of nodes/child nodes and even thousands.

                please give me some advice in this case what i have to do?

                would appreciate if any one could help me to slove this problem,thanks in advance.

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  andre
                  wrote on 16 Jul 2011, 08:45 last edited by
                  #8

                  The solution I presented above was used in the context of a very shallow tree with only a few dozen nodes. In that context, it was not worth investing time in optimizing it. It is certainly not suitable as-is for use cases with thousands of nodes or with deep structures. For that, you will need to optimize it. View the code I posted as a basic example of how you can do this, not how to do it efficiently.

                  Lazy loading basically means that your QAIM does not know about the full structure of your tree when it is created. It will only load new parts of the tree as they are requested. The QFileSystemModel is a good example. It will not scan your entire file system before showing nodes in the tree. Instead, it will only scan what is needed, and use that. Note that having your nodes not be expanded is not the same. However, lazy loading models are an exception, not the rule. They are not trivial to build. If you don't know what they are, chances are your model is not lazy loaded :-) QStandardItemModel is a non-lazy loaded model, but certainly not the only one.

                  The problem at hand: filtering a tree in such a way that you also show matching child nodes even if their parent does not by itself match, does not lend itself to lazy-loaded trees. They always need an evaluation of the whole depth of the tree (worst case, of course), and as you can not compare your criteria with nodes that have not been loaded yet, this does not work for lazy-loaded models.

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    ManasQt
                    wrote on 18 Jul 2011, 13:59 last edited by
                    #9

                    Hi Andre, thanks for the clear explanation. I Will give a shot at QStandardItemModel which is non-lazy loaded model and I will get back to you later.
                    Have a wonderful day.

                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      ManasQt
                      wrote on 20 Jul 2011, 20:43 last edited by
                      #10

                      Hi Andre,
                      I have created TreeView based on QStandardItemModel,QStandardItem and added QSortFilterProxyModel but while I start searching/Filtering still I can see some delay and I have to call QTreeView.expandAll() every time.

                      Donno how to solve this problem.

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        andre
                        wrote on 21 Jul 2011, 05:12 last edited by
                        #11

                        Please be a bit more clear in your description. Are you using a QSPM, or are you using the proxy model I presented above? How many items do you have in your tree? How deeply nested? Do you need to call expandAll for the filtering to work, or for the results of the search to be visible?

                        1 Reply Last reply
                        0
                        • M Offline
                          M Offline
                          ManasQt
                          wrote on 21 Jul 2011, 15:04 last edited by
                          #12

                          hi Andre,

                          bq. Q1)Are you using a QSPM, or are you using the proxy model I presented above?

                          Created Tree Node by sub classing QStandardItem.

                          Created model by sub classing QStandardItemModel .

                          Applied sorting and filtering using sub classing QSortFilterPrxoyModel and implemented "filterAcceptsRow"

                          Attached proxymodel to QTreeView

                          bq. Q2)How many items do you have in your tree?

                          2000 items

                          bq. Q3)How deeply nested?

                          QTreeview is basically only 4 level deep

                          bq. Q4)Do you need to call expandAll for the filtering to work, or for the results of the search to be visible?

                          I am using a QLineEdit widget for search/filter based on its "textChanged" Signal .While i start typing the resulted items in QTreeView are shown in collapsed mode every time and for this reason i have to call QTreeView.expandAll() always in "editingFinished" signal by pressing 'EnterKey'.

                          hope this will helps you to understand .

                          would appreciate if any one could help me to slove this problem,thanks in advance.

                          1 Reply Last reply
                          0
                          • X Offline
                            X Offline
                            xcround
                            wrote on 9 Aug 2011, 19:53 last edited by
                            #13

                            Hi I'm having a similar problem, where I can only select the parent nodes.
                            I have the following:
                            @
                            bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent)
                            {
                            ....
                            if (!action.isEmpty())
                            {
                            QModelIndex index = sourceModel()->index(sourceRow,
                            ACTION, sourceParent);

                                for (int i = 0; i < sourceModel()->rowCount(sourceParent); i++)
                                {
                                    QModelIndex tmpIndex = sourceModel()->index(i,0,sourceParent);
                                    if (tmpIndex.isValid())
                                        hasAcceptedChildren(tmpIndex.row(), tmpIndex);
                                }
                            
                                if (action != sourceModel()->data(index).toString())
                                    return false;
                            }
                            

                            ....
                            }
                            @

                            Isn't this suppose to be it?
                            Thanks.

                            1 Reply Last reply
                            0
                            • M Offline
                              M Offline
                              ManasQt
                              wrote on 9 Aug 2011, 20:46 last edited by
                              #14

                              Hi xcround,

                                   I am not clear , are you trying to filter parent as well as child?  
                              

                              I have accomplished this and here is the logic,

                              @def filterAcceptsRow(self,sourceRow,sourceParent):
                              if super(MySortFilterProxyModel,self).filterAcceptsRow(sourceRow,sourceParent):
                              return True
                              return self.hasAcceptedChildren(sourceRow,sourceParent)

                              def hasAcceptedChildren(self,sourceRow,sourceParent):
                              model=self.sourceModel()
                              sourceIndex=model.index(sourceRow,0,sourceParent)
                              if not sourceIndex.isValid():
                              return False
                              indexes=model.rowCount(sourceIndex)
                              for i in range(indexes):
                              if self.filterAcceptsRow(i,sourceIndex):
                              return True
                              return False

                              @

                              1 Reply Last reply
                              0
                              • X Offline
                                X Offline
                                xcround
                                wrote on 9 Aug 2011, 20:50 last edited by
                                #15

                                Yes, I would like to filter both parent and child.
                                Also I forgot to mention, I have a return true in the end of the function.
                                Thanks

                                1 Reply Last reply
                                0
                                • X Offline
                                  X Offline
                                  xcround
                                  wrote on 9 Aug 2011, 21:01 last edited by
                                  #16

                                  Here is the what I have now, but still is not filtering the parents and child.
                                  @
                                  bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent)
                                  {
                                  ....
                                  if (!action.isEmpty())
                                  {

                                      QModelIndex index = sourceModel()->index(sourceRow,
                                                                               ACTION, sourceParent);
                                  
                                      if (hasAcceptedChildren(sourceRow, sourceParent))
                                          return true;
                                  
                                      if (action != sourceModel()->data(index).toString())
                                          return false;
                                  }
                                  return true;
                                  

                                  }
                                  bool TableProxy::filterAcceptsRowItself(int sourceRow, const QModelIndex &sourceParent) const
                                  {
                                  return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
                                  }

                                  bool TableProxy::hasAcceptedChildren(int source_row, const QModelIndex &source_parent) const
                                  {
                                  QModelIndex item = source_parent;
                                  if (!item.isValid())
                                  {
                                  qDebug() << "item invalid" << source_parent.data().toString() << source_row;
                                  return false;
                                  }
                                  int childCount = item.model()->rowCount(item);

                                  if (childCount == 0)
                                      return false;
                                  
                                  for (int i = 0; i < childCount; ++i)
                                  {
                                      if (filterAcceptsRowItself(i, item))
                                          return true;
                                      //recursive call
                                      if (hasAcceptedChildren(i, item))
                                          return true;
                                  }
                                  
                                  return false;
                                  

                                  }
                                  @
                                  Thanks for the help

                                  1 Reply Last reply
                                  0
                                  • M Offline
                                    M Offline
                                    ManasQt
                                    wrote on 9 Aug 2011, 21:06 last edited by
                                    #17

                                    Hi xcround,
                                    please make changes in your code according this python code and i am sure it will filter parent as well as child .
                                    @
                                    def filterAcceptsRow(self,sourceRow,sourceParent):
                                    if super(MySortFilterProxyModel,self).filterAcceptsRow(sourceRow,sourceParent):
                                    return True
                                    return self.hasAcceptedChildren(sourceRow,sourceParent)

                                    def hasAcceptedChildren(self,sourceRow,sourceParent):
                                    model=self.sourceModel()
                                    sourceIndex=model.index(sourceRow,0,sourceParent)
                                    if not sourceIndex.isValid():
                                    return False
                                    indexes=model.rowCount(sourceIndex)
                                    for i in range(indexes):
                                    if self.filterAcceptsRow(i,sourceIndex):
                                    return True
                                    return False@

                                    1 Reply Last reply
                                    0
                                    • M Offline
                                      M Offline
                                      ManasQt
                                      wrote on 9 Aug 2011, 21:22 last edited by
                                      #18

                                      Hi xcround,
                                      I think in your case it would be some thing like this needs to be......
                                      ( i am not a c/c++ guy)
                                      @
                                      bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent)
                                      {
                                      ....
                                      if (!action.isEmpty())
                                      {
                                      if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent))
                                      {
                                      return true
                                      }
                                      return hasAcceptedChildren(sourceRow, sourceParent)
                                      }
                                      .........
                                      }

                                      bool TableProxy::hasAcceptedChildren(int source_row, const QModelIndex &source_parent) const
                                      {
                                      QModelIndex item = source_parent;
                                      if (!item.isValid())
                                      {
                                      qDebug() << "item invalid" << source_parent.data().toString() << source_row;
                                      return false;
                                      }
                                      int childCount = item.model()->rowCount(item);

                                      if (childCount == 0)
                                          return false;
                                      
                                      for (int i = 0; i < childCount; ++i)
                                      {
                                          if (filterAcceptsRow(i, item))
                                              return true;
                                      }
                                      
                                      return false;
                                      

                                      }@

                                      1 Reply Last reply
                                      0
                                      • X Offline
                                        X Offline
                                        xcround
                                        wrote on 9 Aug 2011, 21:44 last edited by
                                        #19

                                        Sorry but from mine understanding I first need to check if the string matches the one I'm looking for.
                                        So that's why I have the first if statement, and then I do what you said in the previous post.
                                        @
                                        f (!action.isEmpty())
                                        {
                                        QModelIndex index = sourceModel()->index(sourceRow,
                                        ACTION, sourceParent);

                                                if (action != sourceModel()->data(index).toString())
                                                    return false;
                                        
                                                if ((QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)))
                                                    return true;
                                        
                                                return hasAcceptedChildren(sourceRow, sourceParent);
                                            }
                                        

                                        @
                                        Still trying to understand whats going wrong.

                                        1 Reply Last reply
                                        0
                                        • B Offline
                                          B Offline
                                          burgerking
                                          wrote on 13 Dec 2011, 13:51 last edited by
                                          #20

                                          Hi qties,

                                          at the first time I read the post from André I was so happy to find the solution I am searching for.
                                          But unfortunately I am to stupid to get it work. Maybe you can help me.

                                          If I use the standard QSortFilterProxyModel everything works good except the search at textChanged Signal.
                                          But if I use the LeafFilterProxyModel (exactly as André has post it) an error occurs and I do not know what it means.

                                          I hope somebady can help me.
                                          Thank you.

                                          @
                                          LeafFilterProxyModel m_oProxyModel;
                                          //QSortFilterProxyModel m_oProxyModel;
                                          QStandardItemModel m_oItemModel; // manualy filled

                                          m_oProxyModel.setSourceModel(&m_oItemModel);
                                          m_tvDBC->setModel(&m_oProxyModel); // TreeView
                                          m_lvDBC->setModel(&m_oStringModel); // ListView
                                          @

                                          @
                                          void cClass::textChanged(QString text)
                                          {
                                          QRegExp::PatternSyntax pSyntax = QRegExp::PatternSyntax(QRegExp::FixedString);
                                          QRegExp regExp(text, Qt::CaseInsensitive, pSyntax);

                                          m_oProxyModel.setFilterRegExp(regExp);
                                          m_oProxyModel.setFilterKeyColumn(-1);
                                          }
                                          @

                                          @
                                          Error 1 error LNK2001: unresolved external symbol "public: __thiscall
                                          LeafFilterProxyModel::LeafFilterProxyModel(class QObject *)"
                                          ??0LeafFilterProxyModel@@QAE@PAVQObject@@@Z) myobject.obj
                                          @

                                          1 Reply Last reply
                                          0

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved