一、描述
QSortFilterProxyModel 可用于对项目进行排序、过滤。该模型而无需对底层数据进行任何转换,也无需复制内存中的数据。
要对自定义模型提供的项目进行排序和过滤。需要创建一个 QSortFilterProxyModel,以 MyItemModel 作为参数调用 setSourceModel(),并在视图上安装 QSortFilterProxyModel:
QTreeView *treeView = new QTreeView;
MyItemModel *sourceModel = new MyItemModel(this);
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(sourceModel);
treeView->setModel(proxyModel);
原始数据显示在视图中。通过 QSortFilterProxyModel 所做的任何更改都会应用于原始模型。
QSortFilterProxyModel 充当原始模型的包装器。源 QModelIndexes 与排序/过滤的模型索引相互转换请使用 mapToSource()、mapFromSource()、mapSelectionToSource() 、mapSelectionFromSource()。
默认情况下,只要原始模型发生变化,模型就会动态重新排序和重新过滤数据。 可以通过设置 dynamicSortFilter 属性来更改此行为。
二、属性成员
1、autoAcceptChildRows : bool
如果为 true将不会过滤掉接受行的子项(即使它们本身被过滤掉的时候)。默认值为false。
2、dynamicSortFilter : bool
是否在源模型的内容发生变化时动态排序和过滤。
请注意,当 dynamicSortFilter 为 true 时,不应通过代理模型更新源模型。 例如,如果在 QComboBox 上设置代理模型,则使用更新模型的函数(例如 addItem())将无法按预期工作。 另一种方法是将 dynamicSortFilter 设置为 false 并在将项目添加到 QComboBox 后调用 sort()。
3、filterCaseSensitivity : Qt::CaseSensitivity
用于过滤源模型内容的 QRegularExpression 模式是否区分大小写。默认区分大小写。
4、filterKeyColumn : int
用于过滤源模型内容的键从中读取的列。默认值为 0。如果值为 -1,将从所有列中读取键。
5、filterRegularExpression : QRegularExpression
用于过滤源模型内容的 QRegularExpression
通过 QRegularExpression 重载设置此属性会覆盖当前的 filterCaseSensitivity。默认情况下, QRegularExpression 是一个匹配所有内容的空字符串。
如果未设置 QRegularExpression 或空字符串,则将接受源模型中的所有内容。
6、filterRole : int
在过滤项目时用于查询源模型数据的项目角色。默认值为 Qt::DisplayRole。
7、isSortLocaleAware : bool
在对字符串排序时,是否考虑本地因素。默认情况下不考虑(QLocale)。
8、recursiveFilteringEnabled : bool
过滤器是否递归应用于子项,并且对于任何匹配的子项,其父项也将可见。默认值为false。
9、sortCaseSensitivity : Qt::CaseSensitivity
用于在排序时比较字符串的区分大小写设置。默认情况下,排序区分大小写。
10、sortRole : int
对项目进行排序时查询源模型的数据角色。默认值为 Qt::DisplayRole。
三、重要成员函数
1、void invalidate()
使当前的排序和过滤无效。
2、void setFilterFixedString(const QString &pattern)
设置用于过滤源模型内容的字符串。
3、void setFilterWildcard(const QString &pattern)
设置用于过滤源模型内容的通配符表达式。
4、void setFilterWildcard(const QString &pattern)
设置用于过滤源模型内容的通配符表达式。
5、bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent)
如果source_column 和 source_parent 指示的列中的项目应包含在模型中,则返回 true。
默认实现始终返回 true。
6、bool filterAcceptsRow(int source_row, const QModelIndex &source_parent)
如果由给定的 source_row 和 source_parent 指示的行中的项目应包含在模型中,则返回 true。
如果相关项所持有的值与过滤器字符串、通配符字符串或正则表达式匹配,则默认实现将返回 true。
注意:默认情况下,Qt::DisplayRole 用于确定是否应接受该行。 这可以通过设置 filterRole 属性来更改。
7、void invalidateColumnsFilter()
使列的当前过滤无效。
如果正在进行自定义过滤(通过 filterAcceptsColumn()),并且过滤器参数已更改,则应调用此函数。 这与 invalidateFilter() 的不同之处在于它不会调用 filterAcceptsRow(),而只会调用 filterAcceptsColumn()。 如果要隐藏或显示行不变的列,可以使用它代替 invalidateFilter()。
8、void invalidateFilter()
使当前过滤无效。
如果正在进行自定义过滤(例如 filterAcceptsRow()),并且过滤器参数已更改,则应调用此函数。
9、void invalidateRowsFilter()
使行的当前过滤无效。
如果正在进行自定义过滤(通过 filterAcceptsRow()),并且过滤器参数已更改,则应调用此函数。 这与 invalidateFilter() 的不同之处在于它不会调用 filterAcceptsColumn(),而只会调用 filterAcceptsRow()。 如果要隐藏或显示列不变的行,可以使用它代替 invalidateFilter()。
10、bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right)
如果索引 source_left 引用的项的值小于索引 source_right 引用的项的值,则返回 true,否则返回 false。
此函数在排序时用作 < 运算符,并处理以下 QVariant 类型:
- QMetaType::Int
- QMetaType::UInt
- QMetaType::LongLong
- QMetaType::ULongLong
- QMetaType::Float
- QMetaType::Double
- QMetaType::QChar
- QMetaType::QDate
- QMetaType::QTime
- QMetaType::QDateTime
- QMetaType::QString
任何其他类型都将使用 QVariant::toString() 转换为 QString。
默认情况下,与 QModelIndexes 关联的 Qt::DisplayRole 用于比较。 这可以通过设置 sortRole 属性来更改。
注意:传入的索引是源模型的索引。
四、使用示例
1、官方demo:Basic Sort/Filter Model Example
Window::Window()
{
proxyModel = new QSortFilterProxyModel;
sourceView = new QTreeView;
sourceView->setRootIsDecorated(false);
sourceView->setAlternatingRowColors(true);
proxyView = new QTreeView;
proxyView->setRootIsDecorated(false);
proxyView->setAlternatingRowColors(true);
proxyView->setModel(proxyModel);
proxyView->setSortingEnabled(true);
auto sortCaseSensitivityCheckBox = new QCheckBox(tr("区分大小写的排序"));
filterCaseSensitivityCheckBox = new QCheckBox(tr("区分大小写的过滤器"));
filterPatternLineEdit = new QLineEdit;
auto filterPatternLabel = new QLabel(tr("过滤字符:"));
filterPatternLabel->setBuddy(filterPatternLineEdit);
filterSyntaxComboBox = new QComboBox;
filterSyntaxComboBox->addItem(tr("正则表达式"), QRegExp::RegExp);
filterSyntaxComboBox->addItem(tr("通配符"), QRegExp::Wildcard);
filterSyntaxComboBox->addItem(tr("固定字符串"), QRegExp::FixedString);
auto filterSyntaxLabel = new QLabel(tr("过滤器语法:"));
filterSyntaxLabel->setBuddy(filterSyntaxComboBox);
filterColumnComboBox = new QComboBox;
filterColumnComboBox->addItem(tr("Subject"));
filterColumnComboBox->addItem(tr("Sender"));
filterColumnComboBox->addItem(tr("Date"));
auto filterColumnLabel = new QLabel(tr("过滤行:"));
filterColumnLabel->setBuddy(filterColumnComboBox);
connect(filterPatternLineEdit, &QLineEdit::textChanged,this, &Window::filterRegExpChanged);
connect(filterSyntaxComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &Window::filterRegExpChanged);
connect(filterColumnComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &Window::filterColumnChanged);
connect(filterCaseSensitivityCheckBox, &QAbstractButton::toggled,this, &Window::filterRegExpChanged);
connect(sortCaseSensitivityCheckBox, &QAbstractButton::toggled,this, &Window::sortChanged);
auto sourceGroupBox = new QGroupBox(tr("原始模型"));
auto proxyGroupBox = new QGroupBox(tr("排序/过滤模型"));
QHBoxLayout *sourceLayout = new QHBoxLayout;
sourceLayout->addWidget(sourceView);
sourceGroupBox->setLayout(sourceLayout);
QGridLayout *proxyLayout = new QGridLayout;
proxyLayout->addWidget(proxyView, 0, 0, 1, 3);
proxyLayout->addWidget(filterPatternLabel, 1, 0);
proxyLayout->addWidget(filterPatternLineEdit, 1, 1, 1, 2);
proxyLayout->addWidget(filterSyntaxLabel, 2, 0);
proxyLayout->addWidget(filterSyntaxComboBox, 2, 1, 1, 2);
proxyLayout->addWidget(filterColumnLabel, 3, 0);
proxyLayout->addWidget(filterColumnComboBox, 3, 1, 1, 2);
proxyLayout->addWidget(filterCaseSensitivityCheckBox, 4, 0, 1, 2);
proxyLayout->addWidget(sortCaseSensitivityCheckBox, 4, 2);
proxyGroupBox->setLayout(proxyLayout);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(sourceGroupBox);
mainLayout->addWidget(proxyGroupBox);
setLayout(mainLayout);
resize(500, 450);
proxyView->sortByColumn(1, Qt::AscendingOrder);
filterColumnComboBox->setCurrentIndex(1);
filterPatternLineEdit->setText("Andy|Grace");
filterCaseSensitivityCheckBox->setChecked(true);
sortCaseSensitivityCheckBox->setChecked(true);
}
void Window::setSourceModel(QAbstractItemModel *model)
{
proxyModel->setSourceModel(model);
sourceView->setModel(model);
}
void Window::filterRegExpChanged()
{
QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax(filterSyntaxComboBox->itemData(filterSyntaxComboBox->currentIndex()).toInt());
Qt::CaseSensitivity caseSensitivity = filterCaseSensitivityCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;
QRegExp regExp(filterPatternLineEdit->text(), caseSensitivity, syntax);
proxyModel->setFilterRegExp(regExp);
}
void Window::filterColumnChanged()
{
proxyModel->setFilterKeyColumn(filterColumnComboBox->currentIndex());
}
void Window::sortChanged()
{
proxyModel->setSortCaseSensitivity(sortCaseSensitivityCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);
}
2、官方demo:Custom Sort/Filter Model Example
#ifndef MYSORTFILTERPROXYMODEL_H
#define MYSORTFILTERPROXYMODEL_H
#include <QDate>
#include <QSortFilterProxyModel>
class MySortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
MySortFilterProxyModel(QObject *parent = nullptr);
QDate filterMinimumDate() const
{
return minDate;
}
QDate filterMaximumDate() const
{
return maxDate;
}
void setFilterMinimumDate(const QDate &date);
void setFilterMaximumDate(const QDate &date);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
bool dateInRange(const QDate &date) const;
QDate minDate;
QDate maxDate;
};
#endif // MYSORTFILTERPROXYMODEL_H
#include <QtWidgets>
#include "mysortfilterproxymodel.h"
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
void MySortFilterProxyModel::setFilterMinimumDate(const QDate &date)
{
minDate = date;
invalidateFilter();
}
void MySortFilterProxyModel::setFilterMaximumDate(const QDate &date)
{
maxDate = date;
invalidateFilter();
}
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
{
QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);
return (sourceModel()->data(index0).toString().contains(filterRegExp()) ||
sourceModel()->data(index1).toString().contains(filterRegExp()))
&& dateInRange(sourceModel()->data(index2).toDate());
}
bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);
if (leftData.type() == QVariant::DateTime)
{
return leftData.toDateTime() < rightData.toDateTime();
}
else
{
static const QRegularExpression emailPattern("[\\w\\.]*@[\\w\\.]*");
QString leftString = leftData.toString();
if (left.column() == 1)
{
const QRegularExpressionMatch match = emailPattern.match(leftString);
if (match.hasMatch())
leftString = match.captured(0);
}
QString rightString = rightData.toString();
if (right.column() == 1)
{
const QRegularExpressionMatch match = emailPattern.match(rightString);
if (match.hasMatch())
rightString = match.captured(0);
}
return QString::localeAwareCompare(leftString, rightString) < 0;
}
}
bool MySortFilterProxyModel::dateInRange(const QDate &date) const
{
return (!minDate.isValid() || date > minDate) && (!maxDate.isValid() || date < maxDate);
}
使用:
proxyModel->setFilterMinimumDate(fromDateEdit->date());
proxyModel->setFilterMaximumDate(toDateEdit->date());
此模型设置一个日期区间,匹配在此区间内的数据。