Beispiel für ein benutzerdefiniertes Sortier-/Filtermodell
Das Beispiel für ein benutzerdefiniertes Sortier-/Filtermodell veranschaulicht, wie die Unterklasse QSortFilterProxyModel eine erweiterte Sortierung und Filterung durchführt.
Die Klasse QSortFilterProxyModel bietet Unterstützung für das Sortieren und Filtern von Daten, die zwischen einem anderen Modell und einer Ansicht übergeben werden.
Das Modell wandelt die Struktur eines Quellmodells um, indem es die Modellindizes, die es liefert, auf neue Indizes abbildet, die verschiedenen Orten entsprechen, die die Ansichten verwenden können. Auf diese Weise kann ein bestimmtes Quellmodell für die Ansichten umstrukturiert werden, ohne dass die zugrundeliegenden Daten transformiert werden müssen und ohne dass die Daten im Speicher dupliziert werden.
Das Beispiel des benutzerdefinierten Sortier-/Filtermodells besteht aus zwei Klassen:
- Die Klasse
MySortFilterProxyModel
stellt ein benutzerdefiniertes Proxy-Modell bereit. - Die Klasse
Window
stellt das Hauptanwendungsfenster bereit, das das benutzerdefinierte Proxy-Modell zum Sortieren und Filtern eines Standard-Elementmodells verwendet.
Wir werden zunächst einen Blick auf die Klasse MySortFilterProxyModel
werfen, um zu sehen, wie das benutzerdefinierte Proxy-Modell implementiert ist, und dann einen Blick auf die Klasse Window
werfen, um zu sehen, wie das Modell verwendet wird. Schließlich werfen wir einen kurzen Blick auf die Funktion main()
.
MySortFilterProxyModel Klassendefinition
Die Klasse MySortFilterProxyModel
erbt von der Klasse QSortFilterProxyModel.
Da QAbstractProxyModel und seine Unterklassen von QAbstractItemModel abgeleitet sind, gelten viele der Ratschläge zur Unterklassifizierung normaler Modelle auch für Proxy-Modelle.
Andererseits ist es erwähnenswert, dass viele der Standardimplementierungen von Funktionen auf 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 außer Kraft gesetzt 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 in der Lage sein, unsere Daten durch Angabe eines bestimmten Zeitraums zu filtern. Aus diesem Grund implementieren wir die benutzerdefinierten Funktionen setFilterMinimumDate()
und setFilterMaximumDate()
sowie die entsprechenden Funktionen filterMinimumDate()
und filterMaximumDate()
. Wir implementieren die Funktion filterAcceptsRow() von QSortFilterProxyModel neu, um nur Zeilen mit gültigen Daten zu akzeptieren, und QSortFilterProxyModel::lessThan(), um die Absender nach ihren E-Mail-Adressen sortieren zu können. Schließlich implementieren wir eine dateInRange()
Komfortfunktion, mit der wir feststellen können, ob ein Datum gültig ist.
Implementierung der Klasse MySortFilterProxyModel
Der MySortFilterProxyModel
-Konstruktor ist trivial und übergibt den übergeordneten Parameter an den Konstruktor der Basisklasse:
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { }
Die interessantesten Teile der Implementierung von MySortFilterProxyModel
sind die Reimplementierungen der Funktionen filterAcceptsRow() und lessThan() von QSortFilterProxyModel. Werfen wir zunächst einen Blick auf unsere angepasste lessThan()
Funktion.
bool MySortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { QVariant leftData = sourceModel()->data(left); QVariant rightData = sourceModel()->data(right);
Wir wollen 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(); } 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; } }
Wir verwenden QRegularExpression, um ein Muster für die gesuchten Adressen zu definieren. Die Funktion match() gibt ein Objekt QRegularExpressionMatch zurück, das das Ergebnis des Abgleichs enthält. Wenn es eine Übereinstimmung gibt, gibt hasMatch() true zurück. Das Ergebnis der Übereinstimmung kann mit der Funktion captured() von QRegularExpressionMatch abgerufen werden. Die gesamte Übereinstimmung hat den Index 0 und die eingeklammerten Unterausdrücke haben Indizes ab 1 (außer nicht einklammernden Klammern).
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()); }
Von der Funktion filterAcceptsRow() wird hingegen erwartet, dass sie true zurückgibt, 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 durch Angabe eines bestimmten Zeitraums filtern zu können, implementieren wir auch Funktionen zum Abrufen und Setzen des Mindest- und Höchstdatums:
void MySortFilterProxyModel::setFilterMinimumDate(QDate date) { minDate = date; invalidateFilter(); } void MySortFilterProxyModel::setFilterMaximumDate(QDate date) { maxDate = date; invalidateFilter(); }
Die "get"-Funktionen, filterMinimumDate()
und filterMaximumDate()
, sind trivial und als Inline-Funktion in der Header-Datei implementiert.
Damit ist unser benutzerdefiniertes Proxy-Modell fertig. Schauen wir uns nun an, wie wir es in einer Anwendung verwenden können.
Definition der Fensterklasse
Die Klasse CustomFilter
erbt von QWidget und stellt das Hauptfenster der Anwendung in diesem Beispiel dar:
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; QLabel *filterPatternLabel; QLabel *fromLabel; QLabel *toLabel; FilterWidget *filterWidget; QDateEdit *fromDateEdit; QDateEdit *toDateEdit; };
Wir implementieren zwei private Slots, textFilterChanged()
und dateFilterChanged()
, um darauf zu reagieren, dass der Benutzer das Filtermuster, die Groß- und Kleinschreibung oder eines der Daten ändert. Außerdem implementieren wir eine öffentliche Funktion 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 einzustellen (auf die wir später zurückkommen werden). Bei der Erstellung des Hauptanwendungsfensters gehen wir also davon aus, dass bereits ein Quellmodell existiert, und beginnen mit der Erstellung einer Instanz unseres benutzerdefinierten Proxy-Modells:
Window::Window() { proxyModel = new MySortFilterProxyModel(this);
Wir setzen die Eigenschaft dynamicSortFilter, die festlegt, 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.
Im Hauptfenster der Anwendung werden sowohl das Quellmodell als auch das Proxymodell angezeigt. Die Quellansicht ist recht einfach:
sourceView = new QTreeView; sourceView->setRootIsDecorated(false); sourceView->setAlternatingRowColors(true);
Die Klasse QTreeView bietet eine Standard-Modell/Ansicht-Implementierung einer Baumansicht. Unsere Ansicht implementiert eine Baumdarstellung der Elemente im Quellmodell der Anwendung.
sourceLayout->addWidget(sourceView); sourceGroupBox = new QGroupBox(tr("Original Model")); sourceGroupBox->setLayout(sourceLayout);
Die Klasse QTreeView bietet eine Standardmodell/Ansichtsimplementierung einer Baumansicht; unsere Ansicht implementiert eine Baumdarstellung von Elementen im Quellmodell der Anwendung. Wir fügen unser View-Widget zu einem Layout hinzu, das wir in einem entsprechenden Gruppenrahmen installieren.
Die Proxymodellansicht hingegen enthält mehrere Widgets, die die verschiedenen Aspekte der Umwandlung der Datenstruktur des Quellmodells steuern:
filterWidget = new FilterWidget; filterWidget->setText(tr("Grace|Sports")); connect(filterWidget, &FilterWidget::filterChanged, this, &Window::textFilterChanged); filterPatternLabel = new QLabel(tr("&Filter pattern:")); filterPatternLabel->setBuddy(filterWidget); 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(filterWidget, &QLineEdit::textChanged, this, &Window::textFilterChanged); connect(fromDateEdit, &QDateTimeEdit::dateChanged, this, &Window::dateFilterChanged); connect(toDateEdit, &QDateTimeEdit::dateChanged, this, &Window::dateFilterChanged);
Wenn der Benutzer eine der Filteroptionen ändert, müssen wir den Filter explizit neu anwenden. Dies geschieht, indem die verschiedenen Editoren mit Funktionen verbunden werden, die das Proxy-Modell aktualisieren.
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(filterWidget, 1, 1); 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);
Die Sortierung wird von der Ansicht übernommen. Alles, was wir tun müssen, ist, die Sortierung für unsere Proxy-Ansicht zu aktivieren, indem wir die Eigenschaft QTreeView::sortingEnabled setzen (die standardmäßig falsch ist). Dann fügen wir alle Filter-Widgets und die Proxy-Ansicht zu einem Layout hinzu, das wir in einem entsprechenden Gruppenrahmen installieren.
QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(sourceGroupBox); mainLayout->addWidget(proxyGroupBox); setLayout(mainLayout); setWindowTitle(tr("Custom Sort/Filter Model")); resize(500, 450); }
Nachdem wir unsere beiden Gruppenrahmen in ein anderes Layout eingefügt haben, das wir auf unserem Hauptanwendungs-Widget installieren, passen wir schließlich das Anwendungsfenster an.
Wie oben erwähnt, erstellen wir das Quellmodell in der Funktion main
() und rufen die Funktion Window::setSourceModel()
auf, 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() veranlasst das Proxy-Modell, die Daten im angegebenen Modell zu verarbeiten, in diesem Fall das Mail-Modell. Die Funktion setModel(), die das View-Widget von der Klasse QAbstractItemModel erbt, legt das Modell fest, das in der Ansicht dargestellt werden soll. Beachten Sie, dass die letztgenannte Funktion auch ein neues Auswahlmodell erstellt und einstellt.
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 wird die bevorzugte Syntax abgerufen (das Enum FilterWidget::PatternSyntax wird verwendet, um die Bedeutung des gegebenen Musters zu interpretieren), dann wird die bevorzugte Groß- und Kleinschreibung bestimmt. Auf der Grundlage dieser Präferenzen und des aktuellen Filtermusters wird die Eigenschaft filterRegularExpression des Proxy-Modells festgelegt. 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 auch 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 Daten ändert. Wir rufen die neuen Daten von der Benutzeroberfläche ab und rufen die entsprechenden Funktionen auf (die von unserem benutzerdefinierten Proxymodell bereitgestellt werden), um das minimale und maximale Datum des Proxymodells festzulegen. Wie wir oben erklärt haben, wird durch den Aufruf dieser Funktionen auch das Modell 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(createMailModel(&window)); window.show(); return app.exec(); }
Die Funktion createMailModel()
ist eine Komfortfunktion, die zur Vereinfachung des Konstruktors dient. Sie dient lediglich dazu, ein Modell zu erstellen und zurückzugeben, das eine Sammlung von E-Mails beschreibt. Das Modell ist eine Instanz der Klasse QStandardItemModel, d.h. ein generisches Modell zum Speichern von benutzerdefinierten Daten, das normalerweise als Repository für Standard-Qt-Datentypen verwendet wird. Jede E-Mail-Beschreibung wird dem Modell mit addMail()
hinzugefügt, einer weiteren Komfortfunktion. Siehe itemviews/customsortfiltermodel/main.cpp
für Details.
© 2025 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.