tags:

views:

321

answers:

2

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;
    emit 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.

A: 

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() );
Exa
This looks good, but also looks dependent on the user manually clicking a search button or some such. I'm trying to get live updates to the qlistview. I've updated my post to reflect my current progress.
jkyle
+1  A: 

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.

jkyle