Creating a QLineEdit search field for items displayed in a QListView
I want to create a search field that filters the items shown in a QListView. Basically the user could type in "foo" and only items with "foo" in the DisplayRole are shown.
I already have a few ideas on how to do this, but thought I'd ask those more experienced than I.
My idea would be to use some signals and slots to set a filter in the QAbstractItem model and trigger an update() in the QListView.
Are there any helper methods in QListView for filtering I may have missed?
Is there a canonical way of handling this I haven't run across?
edit
Current progress.
I created a public slot called "updateFilter(QString)" in my QFileSystemModel subclass. Then I
connect(myQLineEditSearch, SIGNAL(textChanged(QString)),
myQFileSysModel, SLOT(updateFilter(QString)));
This sets the filter, then in my QFileSystemModel::data(...) method, I have:
void ComponentModel::updateFilter(QString filter)
{
_filter = filter;
e开发者_开发知识库mit layoutChanged();
}
QVariant ComponentModel::data(const QModelIndex &index, int role) const
{
QVariant result;
// if our search filter term is set and the item does not match,
// do not display item data. Searches are case insensitive
if (!_filter.isEmpty() &&
!QFileSystemModel::data(index, Qt::DisplayRole)
.toString().toLower().contains(_filter.toLower()))
{
return result;
}
result = QFileSystemModel::data(index, role);
return result;
}
This is almost there. The "glitch" I'm working on has to do with where the object is displayed. Currently, if I apply a search that matches the 3rd item in the list only the first two rows are rendered as blank. In other words, it still renders the rows for non-matched items.
Answering my own question for reference.
Looks like what is needed here is a QSortFilterProxyModel.
The code looks something like:
QListView *myview = new QListView(this);
MyModel *model = new MyModel(this);
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(model);
myview->setModel(proxy);
myview->setRootIndex(proxy->mapFromSource(model->index(model->rootPath()));
connect(filterLineEdit, SIGNAL(textChanged(QString)),
proxy, SLOT(setFilterFixedString(QString)));
The only issue I'm seeing in this is the rootIndex seems to get reset when you enter a search string. I'll update when I figure that out.
This would work for a QListWidget...
This method saves all found items in a QList from which you can later read them (for example to show them in the same or a new QListView):
void search_for_string( QString search_str )
{
QList<QListWidgetItem*> my_found_items;
for( int i = 0; i < my_list->count(); i++ )
{
QListWidgetItem* current = my_list->item( i );
if( current->text().contains( search_str ) )
{
my_found_items.append( current );
}
}
}
And when pressing on "Search" or whatever, you call it like that:
search_for_string( my_line_edit->text() );
Another solution which works for a QListWidget:
void SpeciePropertiesListWidget::filter(QString filter_string)
{
hide_all();
QList<QListWidgetItem*> matches ( m_list->findItems(filter_string, Qt::MatchFlag::MatchContains) );
for(QListWidgetItem* item : matches)
item->setHidden(false);
}
void SpeciePropertiesListWidget::hide_all()
{
for(int row(0); row < m_list->count(); row++ )
m_list->item(row)->setHidden(true);
}
And then, connect the QLineEdit:
QLineEdit * m_filter_le = new QLineEdit(this);
SpeciePropertiesListWidget * list_widget = new SpeciePropertiesListWidget(this)
connect(m_filter_le, SIGNAL(textEdited(QString)), list_widget, SLOT(refresh_filter(QString)));
精彩评论