Beispiel für ein benutzerdefiniertes Sortier-/Filtermodell
Das Beispiel für ein benutzerdefiniertes Sortier-/Filtermodell veranschaulicht, wie man eine Unterklasse von ` QSortFilterProxyModel ` erstellt, um erweiterte Sortier- und Filterfunktionen durchzuführen.

Die Klasse „ QSortFilterProxyModel “ bietet Unterstützung für das Sortieren und Filtern von Daten, die zwischen einem anderen Modell und einer Ansicht ausgetauscht werden.
Das Modell transformiert die Struktur eines Quellmodells, indem es die von ihm bereitgestellten Modellindizes neuen Indizes zuordnet, die verschiedenen Positionen entsprechen und von den Ansichten verwendet werden können. Dieser Ansatz ermöglicht es, ein gegebenes Quellmodell aus Sicht der Ansichten neu zu strukturieren, ohne dass Transformationen an den zugrunde liegenden Daten erforderlich sind und ohne dass die Daten im Speicher dupliziert werden.
Das Beispiel „Custom Sort/Filter Model“ besteht aus zwei Klassen:
- Die Klasse „
MySortFilterProxyModel“ stellt ein benutzerdefiniertes Proxy-Modell bereit. - Die Klasse „
Window“ stellt das Hauptanwendungsfenster bereit und nutzt das benutzerdefinierte Proxy-Modell, um ein Standard-Elementmodell zu sortieren und zu filtern.
Wir werden uns zunächst die Klasse „ MySortFilterProxyModel “ ansehen, um zu verstehen, wie das benutzerdefinierte Proxy-Modell implementiert ist, und anschließend die Klasse „ Window “, um zu sehen, wie das Modell verwendet wird. Abschließend werfen wir einen kurzen Blick auf die Funktion „ main() “.
Definition der Klasse „MySortFilterProxyModel“
Die Klasse „ MySortFilterProxyModel “ erbt von der Klasse „ QSortFilterProxyModel “.
Da „ QAbstractProxyModel “ und ihre Unterklassen von „ QAbstractItemModel “ abgeleitet sind, gelten viele der Empfehlungen zur Unterklassifizierung normaler Modelle auch für Proxy-Modelle.
Andererseits ist zu beachten, dass viele der Standardimplementierungen von Funktionen in „ QSortFilterProxyModel “ so geschrieben sind, dass sie die entsprechenden Funktionen im jeweiligen Quellmodell aufrufen. Dieser einfache Proxy-Mechanismus muss möglicherweise für Quellmodelle mit komplexerem Verhalten überschrieben werden. In diesem Beispiel leiten wir von der Klasse „ QSortFilterProxyModel “ ab, um sicherzustellen, dass unser Filter einen gültigen Datumsbereich erkennen kann, und um das Sortierverhalten zu steuern.
class MySortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
MySortFilterProxyModel(QObject *parent = nullptr);
QDate filterMinimumDate() const { return minDate; }
void setFilterMinimumDate(QDate date);
QDate filterMaximumDate() const { return maxDate; }
void setFilterMaximumDate(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(QDate date) const;
QDate minDate;
QDate maxDate;
};Wir möchten unsere Daten nach einem bestimmten Zeitraum filtern können. Aus diesem Grund implementieren wir die benutzerdefinierten Funktionen ` setFilterMinimumDate() ` und ` setFilterMaximumDate() ` sowie die entsprechenden Funktionen ` filterMinimumDate() ` und ` filterMaximumDate() `. Wir implementieren die Funktion „ filterAcceptsRow()“ aus „ QSortFilterProxyModel “ neu, damit sie nur Zeilen mit gültigen Datumsangaben akzeptiert, sowie die Funktion „ QSortFilterProxyModel::lessThan()“, um die Absender nach ihren E-Mail-Adressen sortieren zu können. Schließlich implementieren wir eine Hilfsfunktion „ dateInRange() “, mit der wir prüfen können, ob ein Datum gültig ist.
Implementierung der Klasse `MySortFilterProxyModel`
Der Konstruktor von „ MySortFilterProxyModel “ ist trivial und übergibt den „parent“-Parameter an den Konstruktor der Basisklasse:
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}Die interessantesten Teile der Implementierung von ` MySortFilterProxyModel ` sind die Neuimplementierungen der Funktionen ` filterAcceptsRow()` und ` lessThan()` aus ` QSortFilterProxyModel`. Werfen wir zunächst einen Blick auf unsere angepasste Funktion ` lessThan() `.
bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);Wir möchten die Absender nach ihren E-Mail-Adressen sortieren. Die Funktion ` lessThan()` wird beim Sortieren als <-Operator verwendet. Die Standardimplementierung verarbeitet eine Reihe von Typen, darunter ` QDateTime ` und `String`, aber um die Absender nach ihren E-Mail-Adressen sortieren zu können, müssen wir zunächst die Adresse innerhalb der angegebenen Zeichenkette identifizieren:
if (leftData.userType() == QMetaType::QDateTime)
return leftData.toDateTime() < rightData.toDateTime();
static const QRegularExpression emailPattern(u"[\\w\\.]*@[\\w\\.]*"_s);
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;
}Wir verwenden ` QRegularExpression `, um ein Muster für die gesuchten Adressen zu definieren. Die Funktion ` match()` gibt ein ` QRegularExpressionMatch `-Objekt zurück, das das Ergebnis des Abgleichs enthält. Bei einer Übereinstimmung gibt ` hasMatch()` den Wert „true“ zurück. Das Ergebnis des Abgleichs kann mit der Funktion ` captured()` von ` QRegularExpressionMatch` abgerufen werden. Der gesamte Abgleich hat den Index 0, und die in Klammern gesetzten Teilausdrücke haben Indizes ab 1 (mit Ausnahme von Klammern ohne Erfassung).
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(filterRegularExpression())
|| sourceModel()->data(index1).toString().contains(filterRegularExpression()))
&& dateInRange(sourceModel()->data(index2).toDate());
}Die Funktion „ filterAcceptsRow()“ hingegen soll „true“ zurückgeben, wenn die angegebene Zeile in das Modell aufgenommen werden soll. In unserem Beispiel wird eine Zeile akzeptiert, wenn entweder der Betreff oder der Absender den angegebenen regulären Ausdruck enthält und das Datum gültig ist.
bool MySortFilterProxyModel::dateInRange(QDate date) const
{
return (!minDate.isValid() || date > minDate)
&& (!maxDate.isValid() || date < maxDate);
}Wir verwenden unsere benutzerdefinierte Funktion ` dateInRange() `, um festzustellen, ob ein Datum gültig ist.
Um unsere Daten nach einem bestimmten Zeitraum filtern zu können, implementieren wir außerdem Funktionen zum Abrufen und Festlegen des Mindest- und Höchstdatums:
void MySortFilterProxyModel::setFilterMinimumDate(QDate date)
{
beginFilterChange();
minDate = date;
endFilterChange(QSortFilterProxyModel::Direction::Rows);
}
void MySortFilterProxyModel::setFilterMaximumDate(QDate date)
{
beginFilterChange();
maxDate = date;
endFilterChange(QSortFilterProxyModel::Direction::Rows);
}Die Abruf-Funktionen „ filterMinimumDate() “ und „ filterMaximumDate() “ sind trivial und als Inline-Funktionen in der Header-Datei implementiert.
Damit ist unser benutzerdefiniertes Proxy-Modell fertiggestellt. Schauen wir uns nun an, wie wir es in einer Anwendung nutzen können.
Definition der Fensterklasse
Die Klasse „ CustomFilter “ erbt von „ QWidget “ und stellt das Hauptanwendungsfenster dieses Beispiels bereit:
class Window : public QWidget
{
Q_OBJECT
public:
Window();
void setSourceModel(QAbstractItemModel *model);
private slots:
void textFilterChanged();
void dateFilterChanged();
private:
MySortFilterProxyModel *proxyModel;
QTreeView *sourceView;
QTreeView *proxyView;
FilterWidget *filterWidget;
QDateEdit *fromDateEdit;
QDateEdit *toDateEdit;
};Wir implementieren zwei private Slots, „ textFilterChanged() “ und „ dateFilterChanged() “, um auf Änderungen des Filtermusters, der Groß-/Kleinschreibung oder eines der Datumsangaben durch den Benutzer zu reagieren. Zusätzlich implementieren wir eine öffentliche Hilfsfunktion „ setSourceModel() “, um die Beziehung zwischen Modell und Ansicht einzurichten.
Implementierung der Fensterklasse
In diesem Beispiel haben wir uns dafür entschieden, das Quellmodell in der Funktion ` main ()` zu erstellen und festzulegen (darauf werden wir später noch zurückkommen). Beim Aufbau des Hauptanwendungsfensters gehen wir also davon aus, dass bereits ein Quellmodell vorhanden ist, und beginnen mit der Erstellung einer Instanz unseres benutzerdefinierten Proxy-Modells:
Window::Window()
: proxyModel(new MySortFilterProxyModel(this)),Wir setzen die Eigenschaft ` dynamicSortFilter `, die angibt, ob das Proxy-Modell dynamisch sortiert und gefiltert wird. Indem wir diese Eigenschaft auf `true` setzen, stellen wir sicher, dass das Modell sortiert und gefiltert wird, sobald sich der Inhalt des Quellmodells ändert.
Das Hauptanwendungsfenster zeigt Ansichten sowohl des Quellmodells als auch des Proxy-Modells an. Die Quellansicht ist recht einfach:
sourceView->setRootIsDecorated(false);
sourceView->setAlternatingRowColors(true);Die Klasse „ QTreeView “ stellt eine Standard-Modell-/Ansichts-Implementierung einer Baumansicht bereit. Unsere Ansicht implementiert eine Baumdarstellung der Elemente im Quellmodell der Anwendung.
auto *sourceGroupBox = new QGroupBox(tr("Original Model"));
auto *sourceLayout = new QHBoxLayout(sourceGroupBox);
sourceLayout->addWidget(sourceView);Die Klasse ` QTreeView ` bietet eine Standard-Modell/Ansicht-Implementierung einer Baumansicht; unsere Ansicht implementiert eine Baumdarstellung der Elemente im Quellmodell der Anwendung. Wir fügen unser Ansichts-Widget einem Layout hinzu, das wir in einem entsprechenden Gruppenfeld platzieren.
Die Proxy-Modellansicht hingegen enthält mehrere Widgets, die die verschiedenen Aspekte der Transformation der Datenstruktur des Quellmodells steuern:
filterWidget->setText(tr("Grace|Sports"));
fromDateEdit->setDate(QDate(1970, 01, 01));
toDateEdit->setDate(QDate(2099, 12, 31));Beachten Sie, dass wir den Filter jedes Mal explizit neu anwenden müssen, wenn der Benutzer eine der Filteroptionen ändert. Dies geschieht, indem die verschiedenen Editoren mit Funktionen verbunden werden, die das Proxy-Modell aktualisieren.
connect(filterWidget, &FilterWidget::filterChanged,
this, &Window::textFilterChanged);
connect(filterWidget, &QLineEdit::textChanged,
this, &Window::textFilterChanged);
connect(fromDateEdit, &QDateTimeEdit::dateChanged,
this, &Window::dateFilterChanged);
connect(toDateEdit, &QDateTimeEdit::dateChanged,
this, &Window::dateFilterChanged);Die Sortierung wird von der Ansicht übernommen. Wir müssen lediglich die Sortierung für unsere Proxy-Ansicht aktivieren, indem wir die Eigenschaft „ QTreeView::sortingEnabled “ setzen (die standardmäßig auf „false“ gesetzt ist). Anschließend fügen wir alle Filter-Widgets und die Proxy-Ansicht zu einem Layout hinzu, das wir in einem entsprechenden Gruppenfeld einbinden.
proxyView->setRootIsDecorated(false);
proxyView->setAlternatingRowColors(true);
proxyView->setModel(proxyModel);
proxyView->setSortingEnabled(true);
proxyView->sortByColumn(1, Qt::AscendingOrder);
auto *proxyGroupBox = new QGroupBox(tr("Sorted/Filtered Model"));
auto *proxyLayout = new QVBoxLayout(proxyGroupBox);
proxyLayout->addWidget(proxyView);
auto *formLayout = new QFormLayout;
proxyLayout->addLayout(formLayout);
formLayout->addRow(tr("&Filter pattern:"), filterWidget);
formLayout->addRow(tr("F&rom:"), fromDateEdit);
formLayout->addRow(tr("&To:"), toDateEdit);Schließlich passen wir das Anwendungsfenster an, nachdem wir unsere beiden Gruppenfelder in ein weiteres Layout eingefügt haben, das wir in unserem Hauptanwendungs-Widget einbinden.
auto *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(sourceGroupBox);
mainLayout->addWidget(proxyGroupBox);
setWindowTitle(tr("Custom Sort/Filter Model"));
auto screenGeometry = screen()->geometry();
resize(screenGeometry.width() / 2, screenGeometry.height() * 2 / 3);
}Wie oben erwähnt, erstellen wir das Quellmodell in der Funktion ` main ()`, indem wir die Funktion ` Window::setSourceModel() ` aufrufen, damit die Anwendung es verwendet:
void Window::setSourceModel(QAbstractItemModel *model)
{
proxyModel->setSourceModel(model);
sourceView->setModel(model);
for (int i = 0; i < proxyModel->columnCount(); ++i)
proxyView->resizeColumnToContents(i);
for (int i = 0; i < model->columnCount(); ++i)
sourceView->resizeColumnToContents(i);
}Die Funktion ` QSortFilterProxyModel::setSourceModel()` sorgt dafür, dass das Proxy-Modell die Daten im angegebenen Modell – in diesem Fall unserem E-Mail-Modell – verarbeitet. Die Funktion ` setModel()`, die das View-Widget von der Klasse ` QAbstractItemModel ` erbt, legt das Modell fest, das die Ansicht darstellen soll. Beachten Sie, dass letztere Funktion auch ein neues Auswahlmodell erstellt und festlegt.
void Window::textFilterChanged()
{
FilterWidget::PatternSyntax s = filterWidget->patternSyntax();
QString pattern = filterWidget->text();
switch (s) {
case FilterWidget::Wildcard:
pattern = QRegularExpression::wildcardToRegularExpression(pattern);
break;
case FilterWidget::FixedString:
pattern = QRegularExpression::escape(pattern);
break;
default:
break;
}
QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption;
if (filterWidget->caseSensitivity() == Qt::CaseInsensitive)
options |= QRegularExpression::CaseInsensitiveOption;
QRegularExpression regularExpression(pattern, options);
proxyModel->setFilterRegularExpression(regularExpression);
}Die Funktion „ textFilterChanged() “ wird immer dann aufgerufen, wenn der Benutzer das Filtermuster oder die Groß-/Kleinschreibung ändert.
Zunächst ermitteln wir die bevorzugte Syntax (die Aufzählung `FilterWidget::PatternSyntax` wird verwendet, um die Bedeutung des angegebenen Musters zu interpretieren), anschließend legen wir die bevorzugte Groß-/Kleinschreibung fest. Basierend auf diesen Einstellungen und dem aktuellen Filtermuster setzen wir die Eigenschaft ` filterRegularExpression ` des Proxy-Modells. Die Eigenschaft ` filterRegularExpression ` enthält den regulären Ausdruck, der zum Filtern des Inhalts des Quellmodells verwendet wird. Beachten Sie, dass der Aufruf der Funktion ` setFilterRegularExpression()` von ` QSortFilterProxyModel` ebenfalls das Modell aktualisiert.
void Window::dateFilterChanged()
{
proxyModel->setFilterMinimumDate(fromDateEdit->date());
proxyModel->setFilterMaximumDate(toDateEdit->date());
}Die Funktion ` dateFilterChanged() ` wird immer dann aufgerufen, wenn der Benutzer den Bereich der gültigen Datumswerte ändert. Wir lesen die neuen Datumswerte aus der Benutzeroberfläche aus und rufen die entsprechenden Funktionen (die von unserem benutzerdefinierten Proxy-Modell bereitgestellt werden) auf, um das Mindest- und das Höchstdatum des Proxy-Modells festzulegen. Wie oben erläutert, wird das Modell durch den Aufruf dieser Funktionen ebenfalls aktualisiert.
Die Funktion `Main()`
In diesem Beispiel haben wir die Anwendung vom Quellmodell getrennt, indem wir das Modell in der Funktion ` main ()` erstellt haben. Zuerst erstellen wir die Anwendung, dann das Quellmodell:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.setSourceModel(new MailModel(&window));
window.show();
return QApplication::exec();
}Wir erstellen eine Klasse „ MailModel “, die von „ QRangeModel “ erbt und „QAbstractItemView::headerData()“ neu implementiert, um die korrekten Abschnittsüberschriften zurückzugeben. Sie wird aus einem Array der Struktur „ MailHeader “ befüllt. „ MailHeader “ wird als „ Q_GADGET “ deklariert und verfügt über Eigenschaften, die von „ QRangeModel “ verwendet werden:
struct MailHeader
{
private:
Q_GADGET
Q_PROPERTY(QString subject MEMBER subject)
Q_PROPERTY(QString sender MEMBER sender)
Q_PROPERTY(QDateTime date MEMBER date)
public:
QString subject;
QString sender;
QDateTime date;
};
class MailModel : public QRangeModel
{
public:
explicit MailModel(QObject *parent = nullptr) : QRangeModel(mails, parent) {}
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return Window::tr("Subject");
case 1:
return Window::tr("Sender");
case 2:
return Window::tr("Date");
default:
break;
}
}
return QRangeModel::headerData(section, orientation, role);
}
private:
static const std::array<MailHeader, 10> mails;
};
const std::array<MailHeader, 10> MailModel::mails = {
MailHeader{ u"RE: Sports"_s, u"Petra Schmidt <petras@nospam.com>"_s,
QDateTime(QDate(2007, 01, 05), QTime(12, 01)) },
MailHeader{ u"AW: Sports"_s, u"Rolf Newschweinstein <rolfn@nospam.com>"_s,
QDateTime(QDate(2007, 01, 05), QTime(12, 00)) },
MailHeader{ u"Sports"_s, u"Linda Smith <linda.smith@nospam.com>"_s,
QDateTime(QDate(2007, 01, 05), QTime(11, 33)) },
MailHeader{ u"Re: Accounts"_s, u"Andy <andy@nospam.com>"_s,
QDateTime(QDate(2007, 01, 03), QTime(14, 26)) },
MailHeader{ u"Re: Accounts"_s, u"Joe Bloggs <joe@bloggs.com>"_s,
QDateTime(QDate(2007, 01, 03), QTime(14, 18)) },
MailHeader{ u"Re: Expenses"_s, u"Andy <andy@nospam.com>"_s,
QDateTime(QDate(2007, 01, 02), QTime(16, 05)) },
MailHeader{ u"Expenses"_s, u"Joe Bloggs <joe@bloggs.com>"_s,
QDateTime(QDate(2006, 12, 25), QTime(11, 39)) },
MailHeader{ u"Accounts"_s, u"pascale@nospam.com"_s,
QDateTime(QDate(2006, 12, 31), QTime(12, 50)) },
MailHeader{ u"Radically new concept"_s, u"Grace K. <grace@software-inc.com>"_s,
QDateTime(QDate(2006, 12, 22), QTime(9, 44)) },
MailHeader{ u"Happy New Year!"_s, u"Grace K. <grace@software-inc.com>"_s,
QDateTime(QDate(2006, 12, 31), QTime(17, 03)) }
};© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.