QSortFilterProxyModel 子类化排序研究
本例中重要包含qtablevie,MyModel(QAbstractTableModel的子类),mysortmodel(QSortFilterProxyModel)的子类。
触发排序的途径主要由一下几种:
1.点击QTableView的表头触发排序。
2.直接调用QTableView的函数进行排序:
void QTableView::sortByColumn(int column, Qt::SortOrder order)
3.调用QSortFiterProxyModel的函数进行排序:
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
下面先从第一种方式说起:
1.点击QTableView的表头触发排序
点击表头的调用栈如下图所示:
点击表头之后的鼠标事件处理过程略,触发排序的过程主要是:
void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
{
。。。。。。
emit sortIndicatorChanged(logicalIndex, order);
}
然后触发QTableView的Slot:
void QTableView::sortByColumn(int column)
{
Q_D(QTableView);
if (column == -1)
return;
d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());
}
此函数中检查一下列是否未-1就直接调用model的sort函数,由于我们这里使用了代理类,因此就调用了代理类(QSortFiterProxyModel)的sort函数,下边我们看看代理类中的sort函数时怎么处理的。
void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
{
Q_D(QSortFilterProxyModel);
if (d->dynamic_sortfilter && d->proxy_sort_column == column && d->sort_order == order)
return;
d->sort_order = order;
d->proxy_sort_column = column;
d->update_source_sort_column();
d->sort();
}
代理类中首先检查当前排序的行和排序方式(升序降序)是否跟上一次时一样的,如果时一样的就返回。
如果不一样,则保存这次排序的排序方式和行。
然后调用两个函数update_source_sort_column 和sort函数。
update_source_sort_column 这个函数比较复杂,主要是把数据model(此例中是MyModel)中行和列和代理Model(MySortModel)的行和列对应起来。因为使用代理类排序之后是不会改变数据model中的数据顺序的。
class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
{
Q_DECLARE_PUBLIC(QSortFilterProxyModel)
public:
struct Mapping {
QVector<int> source_rows;
QVector<int> source_columns;
QVector<int> proxy_rows;
QVector<int> proxy_columns;
QVector<QModelIndex> mapped_children;
QHash<QModelIndex, Mapping *>::const_iterator map_iter;
};
mutable QHash<QModelIndex, Mapping*> source_index_mapping;
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
/*!
\internal
*/
void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
{
source_to_proxy.fill(-1);
int proxy_count = proxy_to_source.size();
for (int i = 0; i < proxy_count; ++i)
source_to_proxy[proxy_to_source.at(i)] = i;
}
/*!
Returns \c true if the item in the column indicated by the given \a source_column
and \a source_parent should be included in the model; otherwise returns \c false.
The default implementation returns \c true if the value held by the relevant item
matches the filter string, wildcard string or regular expression.
\note By default, the Qt::DisplayRole is used to determine if the column
should be accepted or not. This can be changed by setting the \l
filterRole property.
\sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
*/
bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
{
Q_UNUSED(source_column);
Q_UNUSED(source_parent);
return true;
}
bool QSortFilterProxyModelPrivate::filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const
{
Q_Q(const QSortFilterProxyModel);
return filter_recursive
? filterRecursiveAcceptsRow(source_row, source_parent)
: q->filterAcceptsRow(source_row, source_parent);
}
IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping(
const QModelIndex &source_parent) const
{
Q_Q(const QSortFilterProxyModel);
IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
if (it != source_index_mapping.constEnd()) // was mapped already
return it;
Mapping *m = new Mapping;
int source_rows = model->rowCount(source_parent);
m->source_rows.reserve(source_rows);
for (int i = 0; i < source_rows; ++i) {
if (filterAcceptsRowInternal(i, source_parent))
m->source_rows.append(i);
}
int source_cols = model->columnCount(source_parent);
m->source_columns.reserve(source_cols);
for (int i = 0; i < source_cols; ++i) {
if (q->filterAcceptsColumn(i, source_parent))
m->source_columns.append(i);
}
sort_source_rows(m->source_rows, source_parent);
m->proxy_rows.resize(source_rows);
build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
m->proxy_columns.resize(source_cols);
build_source_to_proxy_mapping(m->source_columns, m->proxy_columns);
it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m));
m->map_iter = it;
if (source_parent.isValid()) {
QModelIndex source_grand_parent = source_parent.parent();
IndexMap::const_iterator it2 = create_mapping(source_grand_parent);
Q_ASSERT(it2 != source_index_mapping.constEnd());
it2.value()->mapped_children.append(source_parent);
}
Q_ASSERT(it != source_index_mapping.constEnd());
Q_ASSERT(it.value());
return it;
}
/*!
\internal
update the source_sort_column according to the proxy_sort_column
return true if the column was changed
*/
bool QSortFilterProxyModelPrivate::update_source_sort_column()
{
int old_source_sort_column = source_sort_column;
if (proxy_sort_column == -1) {
source_sort_column = -1;
} else {
// We cannot use index mapping here because in case of a still-empty
// proxy model there's no valid proxy index we could map to source.
// So always use the root mapping directly instead.
Mapping *m = create_mapping(QModelIndex()).value();
if (proxy_sort_column < m->source_columns.size())
source_sort_column = m->source_columns.at(proxy_sort_column);
else
source_sort_column = -1;
}
return old_source_sort_column != source_sort_column;
}
下边接着说sort函数:
class QSortFilterProxyModelLessThan
{
public:
inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent,
const QAbstractItemModel *source,
const QSortFilterProxyModel *proxy)
: sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
// 重载函数调用运算符
//函数调用运算符 () 可以被重载用于类的对象。当重载 () 时,您不是创造了一种新的调用函数的方式,相//反地,这是创建一个可以传递任意数目参数的运算符函数。
//要想完成排序,需要上边的四个参数,但是stable_sort的cmp函数的参数类型是固定的,这里相当于使用此方法绕过了只能传递两个参数的限制,C++ 果然博大精深
inline bool operator()(int r1, int r2) const
{
QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
return proxy_model->lessThan(i1, i2);
}
private:
int sort_column;
QModelIndex source_parent;
const QAbstractItemModel *source_model;
const QSortFilterProxyModel *proxy_model;
};
class QSortFilterProxyModelGreaterThan
{
public:
inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent,
const QAbstractItemModel *source,
const QSortFilterProxyModel *proxy)
: sort_column(column), source_parent(parent),
source_model(source), proxy_model(proxy) {}
inline bool operator()(int r1, int r2) const
{
QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
return proxy_model->lessThan(i2, i1);
}
private:
int sort_column;
QModelIndex source_parent;
const QAbstractItemModel *source_model;
const QSortFilterProxyModel *proxy_model;
};
/*!
\internal
Maps the persistent proxy indexes to source indexes and
returns the list of source indexes.
*/
QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes() const
{
Q_Q(const QSortFilterProxyModel);
QModelIndexPairList source_indexes;
source_indexes.reserve(persistent.indexes.count());
for (QPersistentModelIndexData *data : qAsConst(persistent.indexes)) {
QModelIndex proxy_index = data->index;
QModelIndex source_index = q->mapToSource(proxy_index);
source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index)));
}
return source_indexes;
}
/*!
\internal
Sorts the given \a source_rows according to current sort column and order.
*/
void QSortFilterProxyModelPrivate::sort_source_rows(
QVector<int> &source_rows, const QModelIndex &source_parent) const
{
Q_Q(const QSortFilterProxyModel);
if (source_sort_column >= 0) {
if (sort_order == Qt::AscendingOrder) {
QSortFilterProxyModelLessThan lt(source_sort_column, source_parent, model, q);
std::stable_sort(source_rows.begin(), source_rows.end(), lt);
} else {
QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
//stable_sort 稳定排序,主要是当两个值相等时,不管排序的次数如何,结果都是恒定的
//比如https://www.cnblogs.com/xenny/p/9404758.html
std::stable_sort(source_rows.begin(), source_rows.end(), gt);
}
} else { // restore the source model order
std::stable_sort(source_rows.begin(), source_rows.end());
}
}
/*!
\internal
*/
void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
{
source_to_proxy.fill(-1);
int proxy_count = proxy_to_source.size();
for (int i = 0; i < proxy_count; ++i)
source_to_proxy[proxy_to_source.at(i)] = i;
}
/*!
\internal
Sorts the existing mappings.
*/
void QSortFilterProxyModelPrivate::sort()
{
Q_Q(QSortFilterProxyModel);
emit q->layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
QModelIndexPairList source_indexes = store_persistent_indexes();
IndexMap::const_iterator it = source_index_mapping.constBegin();
for (; it != source_index_mapping.constEnd(); ++it) {
QModelIndex source_parent = it.key();
Mapping *m = it.value();
sort_source_rows(m->source_rows, source_parent);
build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
}
update_persistent_indexes(source_indexes);
emit q->layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
}
源码主要就是上面这些,看千遍不如自己跟一遍。
跟踪完源码,个人觉得似乎不太适合在QSortFilterProxyModel 中做多字段排序。
或许可以在数据model中直接排序数据。
至于上边的第2,第3种触发排序的方法,其实已经包含在第一种触发排序的过程中了,就不再赘述了。