Window Class Implementation
In this example, we have chosen to create and set the source model in the main () function (which we will come back to later). So when constructing the main application window, we assume that a source model already exists and start by creating an instance of our custom proxy model:
Window::Window()
{
proxyModel = new MySortFilterProxyModel(this);
proxyModel->setDynamicSortFilter(true);
We set the dynamicSortFilter property that holds whether the proxy model is dynamically sorted and filtered. By setting this property to true, we ensure that the model is sorted and filtered whenever the contents of the source model change.
The main application window shows views of both the source model and the proxy model. The source view is quite simple:
sourceView = new QTreeView;
sourceView->setRootIsDecorated(false);
sourceView->setAlternatingRowColors(true);
The QTreeView class provides a default model/view implementation of a tree view; our view implements a tree representation of items in the application's source model.
sourceLayout->addWidget(sourceView);
sourceGroupBox = new QGroupBox(tr("Original Model"));
sourceGroupBox->setLayout(sourceLayout);
The QTreeView class provides a default model/view implementation of a tree view; our view implements a tree representation of items in the application's source model. We add our view widget to a layout that we install on a corresponding group box.
The proxy model view, on the other hand, contains several widgets controlling the various aspects of transforming the source model's data structure:
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(const QString &)),
this, SLOT(textFilterChanged()));
connect(filterSyntaxComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(textFilterChanged()));
connect(filterCaseSensitivityCheckBox, SIGNAL(toggled(bool)),
this, SLOT(textFilterChanged()));
connect(fromDateEdit, SIGNAL(dateChanged(const QDate &)),
this, SLOT(dateFilterChanged()));
connect(toDateEdit, SIGNAL(dateChanged(const QDate &)),
this, SLOT(dateFilterChanged()));
Note that whenever the user changes one of the filtering options, we must explicitly reapply the filter. This is done by connecting the various editors to functions that update the proxy model.
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);
The sorting will be handled by the view. All we have to do is to enable sorting for our proxy view by setting the QTreeView::sortingEnabled property (which is false by default). Then we add all the filtering widgets and the proxy view to a layout that we install on a corresponding group box.
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(sourceGroupBox);
mainLayout->addWidget(proxyGroupBox);
setLayout(mainLayout);
setWindowTitle(tr("Custom Sort/Filter Model"));
resize(500, 450);
}
Finally, after putting our two group boxes into another layout that we install on our main application widget, we customize the application window.
As mentioned above, we create the source model in the main () function, calling the Window::setSourceModel() function to make the application use it:
void Window::setSourceModel(QAbstractItemModel *model)
{
proxyModel->setSourceModel(model);
sourceView->setModel(model);
}
The QSortFilterProxyModel::setSourceModel() function makes the proxy model process the data in the given model, in this case out mail model. The setModel() that the view widget inherits from the QAbstractItemModel class, sets the model for the view to present. Note that the latter function will also create and set a new selection model.
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);
}
The textFilterChanged() function is called whenever the user changes the filter pattern or the case sensitivity.
We first retrieve the preferred syntax (the QRegExp::PatternSyntax enum is used to interpret the meaning of the given pattern), then we determine the preferred case sensitivity. Based on these preferences and the current filter pattern, we set the proxy model's filterRegExp property. The filterRegExp property holds the regular expression used to filter the contents of the source model. Note that calling QSortFilterProxyModel's setFilterRegExp() function also updates the model.
void Window::dateFilterChanged()
{
proxyModel->setFilterMinimumDate(fromDateEdit->date());
proxyModel->setFilterMaximumDate(toDateEdit->date());
}
The dateFilterChanged() function is called whenever the user modifies the range of valid dates. We retrieve the new dates from the user interface, and call the corresponding functions (provided by our custom proxy model) to set the proxy model's minimum and maximum dates. As we explained above, calling these functions also updates the model.
The Main() Function
In this example, we have separated the application from the source model by creating the model in the main () function. First we create the application, then we create the source model:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.setSourceModel(createMailModel(&window));
window.show();
return app.exec();
}
The createMailModel() function is a convenience function provided to simplify the constructor. All it does is to create and return a model describing a collection of emails. The model is an instance of the QStandardItemModel class, i.e., a generic model for storing custom data typically used as a repository for standard Qt data types. Each mail description is added to the model using addMail(), another convenience function. See main.cpp for details.