PDA

View Full Version : QTreeView Filter



y.s.bisht
19th December 2011, 17:17
Hi All,

I want to implement a filter for QTreeView.



-Apple
|____ Object_01
|____ Object_02
|____ XYZ
-Banana
|____ Object_03
|____ Lemon
- Orange
|____ Rabbit
|____ Object_04
|____ Object_05


I have to use a LineEdit for dynamic filter. (Connection to textchange I have done)
Conditions:
1> With every Stroke of letter whole tree should be filtered.
e.g. If in lineEdit I say Obj it should show, important thing is every word in the list should start with Obj.



-Apple
|____ Object_01
|____ Object_02
-Banana
|____ Object_03
- Orange
|____ Object_04
|____ Object_05



I tried using simple filter example(BasisSortFilter) it works perfectly fine for tree with only parents and no children. However it's not working with tree with childrens (above example)
This quote I read while going through the documentation (http://developer.qt.nokia.com/doc/qt-4.8/qsortfilterproxymodel.html) of qsortfilterproxymodel.

For hierarchical models, the filter is applied recursively to all children. If a parent item doesn't match the filter, none of its children will be shown.


This slot is connected to lineEdit with



connect(FilterLineEdit, SIGNAL(textChanged(QString)),this, SLOT(filterChanged()));




void FilterDialog::filterChanged()
{
QRegExp::PatternSyntax syntax = QRegExp::RegExp;
Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
QString strPattern = "^" + FilterLineEdit->text();
QRegExp regExp(strPattern, caseSensitivity);
proxyModel->setFilterRegExp(regExp);
}



Thanks in advance.

Lykurg
19th December 2011, 18:02
Well it is up to you. Subclass QSortFilterProxyModel and make sure parents get only invalid if no children passes the test.

y.s.bisht
19th December 2011, 22:44
Hi Lykurg,

Thanks for your reply, can you provide some code for that ?


Thanks

Lykurg
19th December 2011, 22:49
No, but simply subclass and reimplement QSortFilterProxyModel::filterAcceptsRow(). The detailed description is good and there are some examples in the documentation as well. Try it first yourself, and if you can't manage it, post the code you have such far and we work on from that point.

y.s.bisht
20th December 2011, 09:26
This problem is solved, I have put code for reference.

Thanks to everyone, this forum rocks...




QAbstractItemModel *FilterDialog::createModel(QObject *parent)
{

QStandardItemModel *model = new QStandardItemModel(5, 1, parent);
for( int r=0; r<5; r++ )
{
QStandardItem *item = new QStandardItem( QString("Row:%0").arg(r));

for( int i=0; i<3; i++ )
{
QStandardItem *child = new QStandardItem( QString("Item %0").arg(i) );
child->setEditable( false );
item->appendRow( child );
}

model->setItem(r, 0, item);
}

model->setHorizontalHeaderItem( 0, new QStandardItem( "Column 1" ) );

return model;
}




FilterDialog::FilterDialog(QWidget *parent)
: QDialog(parent)
{
setupUi(this);
proxyModel = new SongTreeProxyFilter;
proxyModel->setDynamicSortFilter(true);
treeView->setModel(proxyModel);
QAbstractItemModel * itemModel = createModel(this);
proxyModel->setSourceModel(itemModel);

connect(FilterLineEdit, SIGNAL(textChanged(QString)),
this, SLOT(filterChanged()));

}

void FilterDialog::filterChanged()
{
QRegExp::PatternSyntax syntax = QRegExp::RegExp;

Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
QString strPattern = "^" + FilterLineEdit->text();
QRegExp regExp(strPattern, caseSensitivity);

proxyModel->setFilterRegExp(regExp);
}





//SubClass of QSortFilterProxyModel
bool TreeProxyFilter::filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
return ShowThis(index);
}

bool TreeProxyFilter::ShowThis(const QModelIndex index) const
{
bool retVal = false;
//Gives you the info for number of childs with a parent
if ( sourceModel()->rowCount(index) > 0 )
{
for( int nChild = 0; nChild < sourceModel()->rowCount(index); nChild++)
{
QModelIndex childIndex = sourceModel()->index(nChild,0,index);
if ( ! childIndex.isValid() )
break;
retVal = ShowThis(childIndex);
if (retVal)
{
break;
}
}
}
else
{
QModelIndex useIndex = sourceModel()->index(index.row(), 0, index.parent());
QString type = sourceModel()->data(useIndex, Qt::DisplayRole).toString();
if ( ! type.contains(filterRegExp()))
{
retVal = false;
}
else
{
retVal = true;
}
}
return retVal;
}

Bycross028
17th December 2014, 11:37
Esta implementación busca si los hijos cumple la condición y no utiliza funciones recursivas


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;
}

t_3
27th January 2015, 10:28
the "filterAcceptsRow" method seems to be meant for recursion, so the shortest way would be (given colum 0 needs to be checked):


bool MySortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
QModelIndex index = sourceModel()->index(row, 0, parent);

if (!index.isValid())
return false;

if (index.data().toString().contains(filterRegExp()))
return true;

int rows = sourceModel()->rowCount(index);
for (row = 0; row < rows; r++)
if (filterAcceptsRow(row, index))
return true;

return false;
}

d_stranz
25th December 2018, 16:21
If I understand your question correctly, what you are saying is that if you return "false" for a certain row, then the children of that row are ignored, right? Unfortunately, that is how QSortFilterProxyModel works - as soon as one level of a tree returns false for filterAcceptsRow(), that row and all of its children are removed from further consideration.

Think about what it would mean if it didn't work that way and continued the recursion into children. What kind of tree would that result in? A bunch of children, but no parent for those children because the parent row had been rejected.

You probably need to rethink how you really want your filtering to work and maybe work out a few test cases by hand so you can get the logic right.

anda_skoa
25th December 2018, 16:58
In such a case it might be easier to implement the filtering in the model itself.

Using a proxy looks convenient at first but it often has actually higher complexity due to working in such an abstracted way.

Cheers,
_