QSortFilterProxyModel filter by IDs [Solved]
-
I have some QTableView with QTableModel. I need to do some filtering for rows. I have some ID that I need to show(stored in QSet<int>) others need to be filtered. I find description in Assistant that QSortFilterProxyModel can do sorting and filtering. I already add sorting for table but I don't know how to do filtering with set of IDs(not wit ReqgExp). Maybe I need to reimplement some virtual function of QSortFilterProxyModel ? Also is there a solution to make fast enable/disable of filter ?
-
You need to reimplement this method
@
bool QSortFilterProxyModel::filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
@About fast enabling/disabling. If you have tons of data and you are time-critical, not memory-critical on filtering, than you can use two proxy models: one with always switched on filtering and one with always switched off filtering and swap them in your view.
-
[quote author="Denis Kormalev" date="1306304398"]You need to reimplement this method
@
bool QSortFilterProxyModel::filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
@About fast enabling/disabling. If you have tons of data and you are time-critical, not memory-critical on filtering, than you can use two proxy models: one with always switched on filtering and one with always switched off filtering and swap them in your view.[/quote]
I have no time- or memory-critical app. I mean how to easy one-click enabled/disable ? I doesn't use QSortFilterProxyModel before. If i write something like this:
@proxyModel->setFilterRegExp(QRegExp(".png", Qt::CaseInsensitive,
QRegExp::FixedString));@I don't enter to filterAcceptsRow function and cant reach my breakpoint. So any code inside is ignored.
-
You won't need that setFilterRegExp. Did you subclass QSortFilterProxyModel as Denis suggested, and reimplemented the filterAcceptsRow method in your subclass?
What you might also do, depending on what filtering you really need to do, is add your ID as a custom role in your model. Then, you can use setFilterRole to point to that custom role you stored the ID under, and use the regular filtering methods. That will break of course if you have a need to "show ID's 1, 2, 4, 8, and 14" or something like that, but will work just fine if you just need one or ranges you could easily express in a regular expression.
-
Yes, I actually need to "show ID’s 1, 2, 4, 8, and 14".
Here what I done in QTableView constructor:
@ m_model = new EventModel(this);
m_proxy = new EventFilterProxyModel();m_proxy->setSourceModel(m_model);
m_proxy->setDynamicSortFilter(true);setModel(m_proxy);
setSortingEnabled(true);@
EventFilterProxyModel is class inherited QSortFilterProxyModel :
@class EventFilterProxyModel : public QSortFilterProxyModel
{
protected :
bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent )
{
if(source_row<5)
return false;return true;
}
};@
And EventModel is class inherits QStandardItemModel. But I don't enter filterAceptsRow at all. -
[quote author="Anticross" date="1306307935"]Yes, I actually need to "show ID’s 1, 2, 4, 8, and 14".
[/quote]
OK, so my suggestion is not an option :-)[quote]
And EventModel is class inherits QStandardItemModel. But I don't enter filterAceptsRow at all.[/quote]filterAcceptsRow is a const method in the base class...
Edit:
Note that I would not rely on the the row number for an ID. If you insert another proxy later on, you're in trouble. Instead, I would add the ID as a role in the base model, and filter based on that. Much more robust, and easier to re-use your proxy model later on. -
So here is proxy filter
@class EventFilterProxyModel : public QSortFilterProxyModel
{
QSet<int> m_set;
bool m_filter;//--------------------------------------------------------------------------------------------------------------
public:
EventFilterProxyModel()
{
m_filter = false;
}
//--------------------------------------------------------------------------------------------------------------
public:
void initIncludes(const QSet<int> & includes){
m_set = (includes);
}
//--------------------------------------------------------------------------------------------------------------
public:
void enableFilter(bool enabled)
{
m_filter = enabled;
}
//--------------------------------------------------------------------------------------------------------------
protected :
bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
{
QModelIndex index = sourceModel()->index(source_row,1,source_parent);int ID = sourceModel()->data(index,Qt::UserRole).toInt();
if(m_filter)
{
if(m_set.contains(ID))
return true;
else
return false;
}return true;
}
//--------------------------------------------------------------------------------------------------------------
};@And a QTableView slot from where I apply filter end init Includes.
@
void EventTable::onFilter( bool enable )
{
if(enable){
EventFilterDialog dlg(this);
if (dlg.exec() != QDialog::Accepted)
{
emit check(false); // here we uncheck filter action
m_proxy->enableFilter(false);return;
}
m_proxy->enableFilter(true);
m_proxy->initIncludes(getIDs());
}
else
m_proxy->enableFilter(false);// here we disable filter}@
But it doesn't work correctly.
-
Looks generally ok, though for maximum re-usability, might I suggest the following adjustments?
- Use the role set with setFilterRole from the QSortFilterProxyModel API. You can retreive it by calling filterRole() in your subclass.
- The same goes for the column you use. Use filterKeyColumn property instead of hardcoding.
- Set enableFilter to true by default in the constructor. Your proxy is a filter proxy, so it makes sense it behaves like one by default.
A bug that is in there, is that you don't tell the view anything has changed if you enable the filter or change the list of accepted ID's. Call QSortFilterProxyModel::invalidateFilter() if you change the enabled status or the list of accepted values.
-
I modified proxy filter:
@class EventFilterProxyModel : public QSortFilterProxyModel
{
QSet<int> m_set;
bool m_filter;//--------------------------------------------------------------------------------------------------------------
public:
EventFilterProxyModel()
{
m_filter = false; // I need to off filter by default
}
//--------------------------------------------------------------------------------------------------------------
public:
void initIncludes(const QSet<int> & includes){
m_set = (includes);
invalidateFilter();
}
//--------------------------------------------------------------------------------------------------------------
public:
void enableFilter(bool enabled)
{
m_filter = enabled;
invalidateFilter();
}
//--------------------------------------------------------------------------------------------------------------
protected :
bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
{
QModelIndex index = sourceModel()->index(source_row,filterKeyColumn(),source_parent);int ID = sourceModel()->data(index,Qt::UserRole).toInt();
if(m_filter)
{
if(m_set.contains(ID))
return true;
else
return false;
}return true;
}
//--------------------------------------------------------------------------------------------------------------
};@And add some code in QTableView contstructor:
@ m_model = new EventModel(this);
m_proxy = new EventFilterProxyModel();m_proxy->setSourceModel(m_model);
m_proxy->setDynamicSortFilter(true);m_proxy->setFilterRole(Qt::UserRole);
m_proxy->setFilterKeyColumn(1);setModel(m_proxy);@
I found that if filter is enabled. Then I show last strings count of QSet<int>. But just last so the filter by IDs don't work. For example if I have 5 records with Ids 1,4,7,8,2 and count of set is 3 it shows only 7,8,2.
-
You still use the hard-coded Qt::UserRole (line 31), perhaps change that since you now set it explicitly in your constructor anyway?
I don't understand what you mean with this:
[quote author="Anticross" date="1306319876"]I modified proxy filter:
I found that if filter is enabled. Then I show last strings count of QSet<int>. But just last so the filter by IDs don't work. For example if I have 5 records with Ids 1,4,7,8,2 and count of set is 3 it shows only 7,8,2.[/quote]What is "count of set"? Do you mean the set with the ID's that you want to show? If you only have three ID's in there, then I thought it was the point of the class to only show those three?
-
[quote author="Andre" date="1306322251"]You still use the hard-coded Qt::UserRole (line 31), perhaps change that since you now set it explicitly in your constructor anyway?
I don't understand what you mean with this:
[quote author="Anticross" date="1306319876"]I modified proxy filter:
I found that if filter is enabled. Then I show last strings count of QSet<int>. But just last so the filter by IDs don't work. For example if I have 5 records with Ids 1,4,7,8,2 and count of set is 3 it shows only 7,8,2.[/quote]What is "count of set"? Do you mean the set with the ID's that you want to show? If you only have three ID's in there, then I thought it was the point of the class to only show those three?
[/quote]
"Count of set" is QSet<int> set; set.count(); :)
I find the reason. It my item holds wrong ID in UserRole. Sonow it's wrked correct after some modification of main model. Thanks everybody. -
OK, great. Glad that you got it working ok.