Qt4.7.0文档翻译:自定义排序/过滤模型示例,Custom Sort/Filter Model Example
文件:
•. itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp
•. itemviews/customsortfiltermodel/customsortfiltermodel.pro
自定义排序 /过滤模型示例展示了如何子类化 QSortFilterProxyModel 以进行高级的排序和过滤 。
QSortFilterProxyModel 类提供对于在另一个模型和一个视图之间传递的数据的排序和过滤的支持 。
这个模型通过将源模型提供的模型索引映射为对应于不同位置的新的索引来实现对源模型的结构的转换,以让视图来使用。对于视图而言 ,这种实现方式允许一个给定的源模型的结构 被改变,而又不需要在底层的数据上作任何变换 ,也不需要在内存中复制数据 。
自定义排序/过滤模型示例包含两个类:
•. MySortFilterProxyModel 类提供一个自定义代理模型 。
•. Window 类提供主程序窗口,它使用那个自定义代理模型来对一个标准的条目模型进行排序和过滤 。
我们首先看一看 MySortFilterProxyModel 类 ,看这个自定义代理模型是怎么实现的,接下来我们看一看 Window 类 ,看看模型是怎么使用的。最后我们会快速地看看 main() 函数 。
MySortFilterProxyModel 类继承了 QSortFilterProxyModel 类 。
由于 QAbstractProxyModel 和它的子类 都继承于 QAbstractItemModel ,所以关于子类化普通模型的大量建议也适用于代理模型 。
另一方面 ,值得注意的是 , QSortFilterProxyModel 中的很多函数的默认实现 都是调用源模型中的等价函数的 。对于有着更复杂行为的源模型 ,这个简单的代理机制可能需要重载;在这个示例里 ,我们从 QSortFilterProxyModel 类派生我们的类,以确保我们的过滤器可以识别一个允许的日期的范围 并且控制排序行为。
class MySortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
MySortFilterProxyModel(QObject *parent = 0);
QDate filterMinimumDate() const { return minDate; }
void setFilterMinimumDate(const QDate &date);
QDate filterMaximumDate() const { return maxDate; }
void setFilterMaximumDate(const QDate &date);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
private:
bool dateInRange(const QDate &date) const;
QDate minDate;
QDate maxDate;
};
我们想要通过指定一个给定的时间段来过滤我们的数据 。由于这个原因 ,我们实现自定义的 setFilterMinimumDate() 和 setFilterMaximumDate() 函数 ,还有对应的 filterMinimumDate() 和 filterMaximumDate() 函数 。我们重载 QSortFilterProxyModel 的 filterAcceptsRow() 函数以实现仅接受包含有效日期的行 ,以及重载 QSortFilterProxyModel::lessThan ()以实现按照发送者的电子邮件地址排序 。最后 ,我们实现一个 dateInRange() 便利函数 ,我们将用它来决定一个日期是否是有效的 。
MySortFilterProxyModel 构造函数是不重要的 ,我们将亲代对象参数传递给 基类的构造函数:
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
MySortFilterProxyModel 的实现的最有趣的部分是对 QSortFilterProxyModel 的 filterAcceptsRow() 和 lessThan() 函数的重载 。让我们首先来看看我们自定义的 lessThan() 函数 。
bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);
我们想要按照发送者的电子邮件地址排序 。 lessThan() 函数在排序的时候 被用作<操作符。默认的实现处理一组包括 QDateTime 和字符串的数据类型,但是为了能够按照发送 者的电子邮件地址排序,我们首先必须识别指定的字符串中的地址 :
if (leftData.type() == QVariant::DateTime) {
return leftData.toDateTime() < rightData.toDateTime();
} else {
QRegExp *emailPattern = new QRegExp("([\w\.]*@[\w\.]*)");
QString leftString = leftData.toString();
if(left.column() == 1 && emailPattern->indexIn(leftString) != -1)
leftString = emailPattern->cap(1);
QString rightString = rightData.toString();
if(right.column() == 1 && emailPattern->indexIn(rightString) != -1)
rightString = emailPattern->cap(1);
return QString::localeAwareCompare(leftString, rightString) < 0;
}
}
我们使用 QRegExp 来为我们寻找的地址定义一个模式 。 QRegExp::indexIn ()函数尝试在给定字符串中找到一个匹配并且返回第一个匹配的位置 ,或者如果没有匹配则返回-1。如果给定的字符串包含这个模式 ,我们就使用 QRegExp 的 cap() 函数来提取实际的地址 。 cap() 函数返回由 第n个 子表达式捕获的文字 。整个匹配的索引是0 , 被括号包围的子表达式的索引从1 开始(除了不起捕获作用的括号以外)。
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());
}
另一方面 ,当给定的行应当被包含在模型中时, filterAcceptsRow() 函数被预期返回真。在我们的示例里 ,如果主 题或者发送者字段里包含给定的正则表达式,并且日期是有效的,则该行被接受。
bool MySortFilterProxyModel::dateInRange(const QDate &date) const
{
return (!minDate.isValid() || date > minDate)
&& (!maxDate.isValid() || date < maxDate);
}
我们使用我们自定义的 dateInRange() 函数来决定一个日期是否有效 。
为了能够通过指定一个给定的时间段来过滤我们的数据,我们还实现了用于获取和设置最小和最大日期的函数 :
void MySortFilterProxyModel::setFilterMinimumDate(const QDate &date)
{
minDate = date;
invalidateFilter();
}
void MySortFilterProxyModel::setFilterMaximumDate(const QDate &date)
{
maxDate = date;
invalidateFilter();
}
用来获取的函数 , filterMinimumDate() 和 filterMaximumDate() ,是不重要的 ,它们是在头文件里以内联函数的方式实现的 。
这就完成了我们的自定义代理模型。让我们来看看我们在程序中可以怎样使用它 。
CustomFilter 类继承了 QWidget ,它提供了这个示例的主程序窗口 :
class Window : public QWidget
{
Q_OBJECT
public:
Window();
void setSourceModel(QAbstractItemModel *model);
private slots:
void textFilterChanged();
void dateFilterChanged();
private:
MySortFilterProxyModel *proxyModel;
QGroupBox *sourceGroupBox;
QGroupBox *proxyGroupBox;
QTreeView *sourceView;
QTreeView *proxyView;
QCheckBox *filterCaseSensitivityCheckBox;
QLabel *filterPatternLabel;
QLabel *fromLabel;
QLabel *toLabel;
QLineEdit *filterPatternLineEdit;
QComboBox *filterSyntaxComboBox;
QDateEdit *fromDateEdit;
QDateEdit *toDateEdit;
};
我们实现了两个私有 槽, textFilterChanged() 和 dateFilterChanged() ,以对 用户改变过滤模式 、大小写敏感或者日期的动作 进行响应 。另外 ,我们实现一个公有的 setSourceModel() 便利函数以设置模型 /视图关系。
在这个示例里 ,我们选择了在 main ()函数 (我们将稍后说到)里创建和设置源模型。所以当我们创建主程序窗口时 ,我们假设一个源模型已经存在了 ,并且直接开始创建我们的自定义代理模型的实例 :
Window::Window()
{
proxyModel = new MySortFilterProxyModel(this);
proxyModel->setDynamicSortFilter(true);
我们设置 dynamicSortFilter 属性,它控制着代理模型是否是动态地排序和过滤的 。通过将这个属性设置为真 ,我们确保了无论何时当源模型的内容发生改变时 ,这个模型都会被排序和过滤。
主程序窗口同时显示针对源模型和代理模型的视图。源视图非常简单:
sourceView = new QTreeView;
sourceView->setRootIsDecorated(false);
sourceView->setAlternatingRowColors(true);
QTreeView 类提供了一个树 型视图的默认的模型 /视图实现;我们的视图实现了对程序的源模型中的条目的一个树 型展现。
sourceLayout->addWidget(sourceView);
sourceGroupBox = new QGroupBox(tr("Original Model"));
sourceGroupBox->setLayout(sourceLayout);
QTreeView 类提供了一个树 型视图的默认的模型 /视图实现;我们的视图实现了对程序的源模型中的条目的一个树 型展现。我们将我们的视图部件添加到在一个对应的编组 框上安装的布局里 。
另一方面,代理模型视图包含了多个部件 ,它们用来控制在将源模型的数据结构进行转换的过程中的各个细节 :
filterCaseSensitivityCheckBox = new QCheckBox(tr("Case sensitive filter"));
filterCaseSensitivityCheckBox->setChecked(true);
filterPatternLineEdit = new QLineEdit;
filterPatternLineEdit->setText("Grace|Sports");
filterPatternLabel = new QLabel(tr("&Filter pattern:"));
filterPatternLabel->setBuddy(filterPatternLineEdit);
filterSyntaxComboBox = new QComboBox;
filterSyntaxComboBox->addItem(tr("Regular expression"), QRegExp::RegExp);
filterSyntaxComboBox->addItem(tr("Wildcard"), QRegExp::Wildcard);
filterSyntaxComboBox->addItem(tr("Fixed string"), QRegExp::FixedString);
fromDateEdit = new QDateEdit;
fromDateEdit->setDate(QDate(1970, 01, 01));
fromLabel = new QLabel(tr("F&rom:"));
fromLabel->setBuddy(fromDateEdit);
toDateEdit = new QDateEdit;
toDateEdit->setDate(QDate(2099, 12, 31));
toLabel = new QLabel(tr("&To:"));
toLabel->setBuddy(toDateEdit);
connect(filterPatternLineEdit, SIGNAL(textChanged(QString)),
this, SLOT(textFilterChanged()));
connect(filterSyntaxComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(textFilterChanged()));
connect(filterCaseSensitivityCheckBox, SIGNAL(toggled(bool)),
this, SLOT(textFilterChanged()));
connect(fromDateEdit, SIGNAL(dateChanged(QDate)),
this, SLOT(dateFilterChanged()));
connect(toDateEdit, SIGNAL(dateChanged(QDate)),
this, SLOT(dateFilterChanged()));
注意每当用户改变其中一个过滤选项时,我们必须显式地重新应用过滤器 。这是通过将那些编辑器连接到更新代理模型的函数上来实现的 。
proxyView = new QTreeView;
proxyView->setRootIsDecorated(false);
proxyView->setAlternatingRowColors(true);
proxyView->setModel(proxyModel);
proxyView->setSortingEnabled(true);
proxyView->sortByColumn(1, Qt::AscendingOrder);
QGridLayout *proxyLayout = new QGridLayout;
proxyLayout->addWidget(proxyView, 0, 0, 1, 3);
proxyLayout->addWidget(filterPatternLabel, 1, 0);
proxyLayout->addWidget(filterPatternLineEdit, 1, 1);
proxyLayout->addWidget(filterSyntaxComboBox, 1, 2);
proxyLayout->addWidget(filterCaseSensitivityCheckBox, 2, 0, 1, 3);
proxyLayout->addWidget(fromLabel, 3, 0);
proxyLayout->addWidget(fromDateEdit, 3, 1, 1, 2);
proxyLayout->addWidget(toLabel, 4, 0);
proxyLayout->addWidget(toDateEdit, 4, 1, 1, 2);
proxyGroupBox = new QGroupBox(tr("Sorted/Filtered Model"));
proxyGroupBox->setLayout(proxyLayout);
排序将会由视图来处理 。所有我们需要做的就是通过设置 QTreeView::sortingEnabled 属性 (这个属性默认为假)来对我们的代理视图启用排序。接下来我们将所有的过滤部件和那个代理视图添加到我们在一个对应的编组 框上安装的布局里。
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(sourceGroupBox);
mainLayout->addWidget(proxyGroupBox);
setLayout(mainLayout);
setWindowTitle(tr("Custom Sort/Filter Model"));
resize(500, 450);
}
最后,在将我们的两个编组 框添加到我们在我们的主程序部件上安装的另一个布局中之后 ,我们自定义了程序窗口。
就像前面提到的那样 ,我们在 main ()函数里创建源模型 ,调用 Window::setSourceModel() 函数以让程序使用它 :
void Window::setSourceModel(QAbstractItemModel *model)
{
proxyModel->setSourceModel(model);
sourceView->setModel(model);
}
QSortFilterProxyModel::setSourceModel ()函数使得代理模型处理给定的模型中的数据 ,在这个例子里就是外发邮件的模型 。视图部件从 QAbstractItemModel 类继承的 setModel() 函数 ,设置要由 该 视图显示的模型 。注意后面的函数会同时创建和设置一个新的选择模型 。
void Window::textFilterChanged()
{
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);
}
每当用户改变过滤模式或者大小写敏感设置时 , textFilterChanged() 函数就 被调用。
我们首先获取选定的语法 ( QRegExp::PatternSyntax 枚举是用来解释给定的模式的意义的),接下来我们获取选定的大小写敏感设置 。基于这些选项和当前的过滤模式 ,我们设置代理模 型 的 filterRegExp 属性。 filterRegExp 属性控制着用来过滤源模型的内容的正则表达式 。注意 , 调用 QSortFilterProxyModel 的 setFilterRegExp() 函数也会更新模型 。
void Window::dateFilterChanged()
{
proxyModel->setFilterMinimumDate(fromDateEdit->date());
proxyModel->setFilterMaximumDate(toDateEdit->date());
}
每当用户修改有效日期的范围时 , dateFilterChanged() 函数就会 被调用。我们从用户界面获取新的日期 ,并且调用对应的函数 (由我们的自定义代理模型提供)来设置代理模型的最小和最大日期 。就像我们前面解释过的 ,调用这些函数也会更新模型 。
在这个示例里 ,我们通过在 main ()函数里创建模型来将程序与源模型隔离 。首先我们创建程序 ,接下来我们创建源模型:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.setSourceModel(createMailModel(&window));
window.show();
return app.exec();
}
createMailModel() 函数是我们提供的一个用于简化构造函数的便利函数 。它所做的就是创建并且返回一个描述了一组电子邮件的模型 。这个模型是 QStandardItemModel 类的一个实例 ,也就是说 ,是一个通用的储存自定义数据的模型 ,典型地 被用作标准的Qt 数据类型的仓库 。每个电子邮件的描述 都 被使用 addMail() 添加到模型中,这是另一个便利函数 。参见 main.cpp 以了解细节 。
Your opinionsHxLauncher: Launch Android applications by voice commands