
Originally Posted by
kerim
caching would be then to store pairs of QModelIndex and QVariants within the subclass of QSortFilterProxyModel ??
Caching is just a concept to speed things up by reusing already calculated data. The way you employ the concept depends on a particular use-case.
i would then connect to the signal (lets say) QAbstractItemModel::rowsAboutToBeInserted
and push the cache to my proxy-model via setData(..) ???
No, definitely not.
Let's assume a very simple example - you have a filter model that works on a simple string that is checked against the contents of the base model - if the string is found in the base model's row, the row is accepted by the filter. If at some point your filter string is "abc" and then you receive a new string - "abcd" then you know that the result will certainly not contain any rows that have been already filtered out by "abc" so you only need to check the rows that contain "abc" but do not contain a trailing "d". Thus you can reuse your earlier (cached) calculations in order to "repair" your result. Remember that what makes QSortFilterProxyModel work is the QSortFilterProxyModel::filterAcceptsRow() method. Here's a sample implementation (not checked with a compiler so it might not build):
Q_OBJECT
public:
IncrementalSortFilterProxyModel(...) : ... {
}
public slots:
void setFilter
(const QString &str
) { recalculateFilter(str):
}
protected:
bool filterAcceptsRow
( int source_row,
const QModelIndex & source_parent
) const { if(m_lastFilter.isEmpty() || m_cache.contains(source_row)) return true;
return false;
}
void recalculateFilter
(const QString &str
) { if(!m_lastFilter.isEmpty() && str.contains(m_lastFilter)) {
// update the cache by removing items
for(QSet<int>::iterator iter = m_cache.begin(); iter!=m_cache.end();) {
if(!idx.data().toString().contains(str))
m_cache.erase(iter); // remove from cache
else ++iter;
}
} else if(!m_lastFilter.isEmpty() && m_lastFilter.contains(str)) {
// update cache by adding items
for(int i=0;i<sourceModel()->rowCount();++i){
if(m_cache.contains(i)) continue;
if(idx.data().toString().contains(str)) m_cache.insert(i);
}
} else {
// recompute everything
m_cache.clear();
if(!str.isEmpty())
for(int i=0;i<sourceModel()->rowCount();++i){
if(idx.data().toString().contains(str)) m_cache.insert(i);
}
}
m_lastFilter = str;
invalidateFilter();
}
private:
QSet<int> m_cache;
};
class IncrementalSortFilterProxyModel : public QSortFilterProxyModel {
Q_OBJECT
public:
IncrementalSortFilterProxyModel(...) : ... {
}
public slots:
void setFilter(const QString &str) {
recalculateFilter(str):
}
protected:
bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const {
if(m_lastFilter.isEmpty() || m_cache.contains(source_row)) return true;
return false;
}
void recalculateFilter(const QString &str) {
if(!m_lastFilter.isEmpty() && str.contains(m_lastFilter)) {
// update the cache by removing items
for(QSet<int>::iterator iter = m_cache.begin(); iter!=m_cache.end();) {
QModelIndex idx = sourceModel()->index(*iter, 0);
if(!idx.data().toString().contains(str))
m_cache.erase(iter); // remove from cache
else ++iter;
}
} else if(!m_lastFilter.isEmpty() && m_lastFilter.contains(str)) {
// update cache by adding items
for(int i=0;i<sourceModel()->rowCount();++i){
if(m_cache.contains(i)) continue;
QModelIndex idx = sourceModel()->index(i, 0);
if(idx.data().toString().contains(str)) m_cache.insert(i);
}
} else {
// recompute everything
m_cache.clear();
if(!str.isEmpty())
for(int i=0;i<sourceModel()->rowCount();++i){
QModelIndex idx = sourceModel()->index(i, 0);
if(idx.data().toString().contains(str)) m_cache.insert(i);
}
}
m_lastFilter = str;
invalidateFilter();
}
private:
QString m_lastFilter;
QSet<int> m_cache;
};
To copy to clipboard, switch view to plain text mode
Of course this works only for simple cases. For complex ones you need to build upon the solution.
BTW. You might even recompute the cache in an external thread and only then call invalidateFilter() to reset the views.
Bookmarks