QSortFilterProxyModel -filterAcceptsRow()- filter parent Node and child Node
-
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.
-
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.
-
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?
-
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.
-
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. -
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@
-
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 -
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@ -
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;
}@
-
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. -
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 filledm_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
@ -
-
There was an unfortunate bug at the time in DevNet, that sometimes inserted ; at the wrong places in code sections. The code listing I posted suffered from this. The argument is supposed to be a single argument source_parent. I'll try to fixup the original posting. Thanks for noticing.
-
Thank you - this thread has been really helpful!
-
Esta implementación recorre todo los sub-Nodos del padre buscando que el filterRegExp coincida con el Qt::DisplayRole del sub-Nodo.
El algoritmo no utiliza recursividad :D
@bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
QList<QModelIndex> children;
children << sourceModel()->index(source_row, 0, source_parent);bool show = false; for(int i = 0; i < children.length(); i++) { if(show) break; // Add sub Nodos // for(int c = 0; c < sourceModel()->rowCount(children[i]) ;c++) children.append(children[i].child(c,0)); QString type = sourceModel()->data(children[i], Qt::DisplayRole).toString(); show = type.contains(filterRegExp()); } return show;
}@