Beispiel für einen Sterndelegierten
Das Beispiel des Sterndelegaten zeigt, wie man einen Delegaten erstellt, der sich selbst zeichnen kann und die Bearbeitung unterstützt.
Wenn Daten in einer QListView, QTableView oder QTreeView angezeigt werden, werden die einzelnen Elemente von einem Delegaten gezeichnet. Wenn der Benutzer mit der Bearbeitung eines Elements beginnt (z. B. durch Doppelklick auf das Element), stellt der Delegat ein Editor-Widget bereit, das während der Bearbeitung über dem Element platziert wird.
Delegaten sind Unterklassen von QAbstractItemDelegate. Qt stellt QStyledItemDelegate zur Verfügung, das von QAbstractItemDelegate erbt und die gebräuchlichsten Datentypen verarbeitet (insbesondere int
und QString). Wenn wir benutzerdefinierte Datentypen unterstützen müssen oder das Rendering oder die Bearbeitung für vorhandene Datentypen anpassen wollen, können wir die Unterklassen QAbstractItemDelegate oder QStyledItemDelegate verwenden. Weitere Informationen über Delegates finden Sie unter Delegate Classes und Model/View Programming, wenn Sie eine Einführung in die Model/View-Architektur von Qt (einschließlich Delegates) auf hohem Niveau benötigen.
In diesem Beispiel werden wir sehen, wie man einen benutzerdefinierten Delegaten implementiert, um einen "Sternbewertungs"-Datentyp zu rendern und zu bearbeiten, der Werte wie "1 von 5 Sternen" speichern kann.
Das Beispiel besteht aus den folgenden Klassen:
StarRating
ist der benutzerdefinierte Datentyp. Er speichert eine in Sternen ausgedrückte Bewertung, z. B. "2 von 5 Sternen" oder "5 von 6 Sternen".StarDelegate
erbt QStyledItemDelegate und bietet Unterstützung fürStarRating
(zusätzlich zu den Datentypen, die bereits von QStyledItemDelegate behandelt werden).StarEditor
erbt QWidget und wird vonStarDelegate
verwendet, um dem Benutzer die Möglichkeit zu geben, eine Sternebewertung mit der Maus zu bearbeiten.
Um StarDelegate
in Aktion zu zeigen, werden wir ein QTableWidget mit einigen Daten füllen und den Delegaten darauf installieren.
StarDelegate Klassendefinition
Hier ist die Definition der Klasse StarDelegate
:
class StarDelegate : public QStyledItemDelegate { Q_OBJECT public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; private slots: void commitAndCloseEditor(); };
Alle öffentlichen Funktionen sind reimplementierte virtuelle Funktionen von QStyledItemDelegate, um benutzerdefiniertes Rendering und Editieren zu ermöglichen.
StarDelegate-Klassenimplementierung
Die Funktion paint() ist eine Neuimplementierung von QStyledItemDelegate und wird immer dann aufgerufen, wenn die Ansicht ein Element neu zeichnen muss:
void StarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); starRating.paint(painter, option.rect, option.palette, StarRating::EditMode::ReadOnly); } else { QStyledItemDelegate::paint(painter, option, index); }
Die Funktion wird für jedes Element, das durch ein QModelIndex Objekt aus dem Modell dargestellt wird, einmal aufgerufen. Wenn es sich bei den im Element gespeicherten Daten um ein StarRating
handelt, malen wir es selbst; andernfalls lassen wir es von QStyledItemDelegate malen. Dadurch wird sichergestellt, dass StarDelegate
die gängigsten Datentypen verarbeiten kann.
Handelt es sich bei dem Element um ein StarRating
, zeichnen wir den Hintergrund, wenn das Element ausgewählt ist, und wir zeichnen das Element mithilfe von StarRating::paint()
, worauf wir später noch eingehen werden.
StartRating
Dank des Makros Q_DECLARE_METATYPE(), das in starrating.h
vorkommt, können Daten in einem QVariant gespeichert werden. Mehr dazu später.
Die Funktion createEditor() wird aufgerufen, wenn der Benutzer mit der Bearbeitung eines Eintrags beginnt:
QWidget *StarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *editor = new StarEditor(parent); connect(editor, &StarEditor::editingFinished, this, &StarDelegate::commitAndCloseEditor); return editor; } return QStyledItemDelegate::createEditor(parent, option, index); }
Wenn das Element ein StarRating
ist, erstellen wir ein StarEditor
und verbinden sein editingFinished()
Signal mit unserem commitAndCloseEditor()
Slot, so dass wir das Modell aktualisieren können, wenn der Editor geschlossen wird.
Hier ist die Implementierung von commitAndCloseEditor()
:
void StarDelegate::commitAndCloseEditor() { StarEditor *editor = qobject_cast<StarEditor *>(sender()); emit commitData(editor); emit closeEditor(editor); }
Wenn der Benutzer mit der Bearbeitung fertig ist, senden wir commitData() und closeEditor() (beide deklariert in QAbstractItemDelegate), um dem Modell mitzuteilen, dass es bearbeitete Daten gibt und um die Ansicht zu informieren, dass der Editor nicht mehr benötigt wird.
Die Funktion setEditorData() wird aufgerufen, wenn ein Editor erstellt wird, um ihn mit Daten aus dem Modell zu initialisieren:
void StarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); StarEditor *starEditor = qobject_cast<StarEditor *>(editor); starEditor->setStarRating(starRating); } else { QStyledItemDelegate::setEditorData(editor, index); } }
Wir rufen einfach setStarRating()
für den Editor auf.
Die Funktion setModelData() wird aufgerufen, um die Daten aus dem Editor in das Modell zu übertragen, wenn die Bearbeitung abgeschlossen ist:
void StarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *starEditor = qobject_cast<StarEditor *>(editor); model->setData(index, QVariant::fromValue(starEditor->starRating())); } else { QStyledItemDelegate::setModelData(editor, model, index); } }
Die Funktion sizeHint()
gibt die bevorzugte Größe eines Elements zurück:
QSize StarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); return starRating.sizeHint(); } return QStyledItemDelegate::sizeHint(option, index); }
Wir leiten den Aufruf einfach an StarRating
weiter.
StarEditor-Klassendefinition
Die Klasse StarEditor
wurde bei der Implementierung von StarDelegate
verwendet. Hier ist die Klassendefinition:
class StarEditor : public QWidget { Q_OBJECT public: StarEditor(QWidget *parent = nullptr); QSize sizeHint() const override; void setStarRating(const StarRating &starRating) { myStarRating = starRating; } StarRating starRating() { return myStarRating; } signals: void editingFinished(); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: int starAtPosition(int x) const; StarRating myStarRating; };
Die Klasse ermöglicht es dem Benutzer, ein StarRating
zu bearbeiten, indem er die Maus über den Editor bewegt. Sie gibt das Signal editingFinished()
aus, wenn der Benutzer auf den Editor klickt.
Die geschützten Funktionen sind von QWidget reimplementiert, um Maus- und Malereignisse zu behandeln. Die private Funktion starAtPosition()
ist eine Hilfsfunktion, die die Nummer des Sterns unter dem Mauszeiger zurückgibt.
Implementierung der StarEditor-Klasse
Lassen Sie uns mit dem Konstruktor beginnen:
StarEditor::StarEditor(QWidget *parent) : QWidget(parent) { setMouseTracking(true); setAutoFillBackground(true); }
Wir aktivieren mouse tracking für das Widget, damit wir dem Cursor folgen können, auch wenn der Benutzer keine Maustaste gedrückt hält. Wir schalten auch QWidget's auto-fill background Funktion ein, um einen undurchsichtigen Hintergrund zu erhalten. (Ohne diesen Aufruf würde der Hintergrund der Ansicht durch den Editor durchscheinen).
Die Funktion paintEvent() wird von QWidget neu implementiert:
void StarEditor::paintEvent(QPaintEvent *) { QPainter painter(this); myStarRating.paint(&painter, rect(), palette(), StarRating::EditMode::Editable); }
Wir rufen einfach StarRating::paint()
auf, um die Sterne zu zeichnen, genau wie wir es bei der Implementierung von StarDelegate
getan haben.
void StarEditor::mouseMoveEvent(QMouseEvent *event) { const int star = starAtPosition(event->position().toPoint().x()); if (star != myStarRating.starCount() && star != -1) { myStarRating.setStarCount(star); update(); } QWidget::mouseMoveEvent(event); }
In der Maus-Ereignishandhabung rufen wir setStarCount()
auf dem privaten Datenelement myStarRating
auf, um die aktuelle Cursorposition wiederzugeben, und wir rufen QWidget::update() auf, um ein erneutes Zeichnen zu erzwingen.
void StarEditor::mouseReleaseEvent(QMouseEvent *event) { emit editingFinished(); QWidget::mouseReleaseEvent(event); }
Wenn der Benutzer eine Maustaste loslässt, wird einfach das Signal editingFinished()
ausgegeben.
int StarEditor::starAtPosition(int x) const { const int star = (x / (myStarRating.sizeHint().width() / myStarRating.maxStarCount())) + 1; if (star <= 0 || star > myStarRating.maxStarCount()) return -1; return star; }
Die Funktion starAtPosition()
verwendet einfache lineare Algebra, um herauszufinden, welcher Stern sich unter dem Mauszeiger befindet.
Definition der StarRating-Klasse
class StarRating { public: enum class EditMode { Editable, ReadOnly }; explicit StarRating(int starCount = 1, int maxStarCount = 5); void paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const; QSize sizeHint() const; int starCount() const { return myStarCount; } int maxStarCount() const { return myMaxStarCount; } void setStarCount(int starCount) { myStarCount = starCount; } void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; } private: QPolygonF starPolygon; QPolygonF diamondPolygon; int myStarCount; int myMaxStarCount; }; Q_DECLARE_METATYPE(StarRating)
Die Klasse StarRating
stellt eine Bewertung als eine Anzahl von Sternen dar. Neben der Speicherung der Daten ist sie auch in der Lage, die Sterne auf einer QPaintDevice zu zeichnen, die in diesem Beispiel entweder eine Ansicht oder ein Editor ist. Die Membervariable myStarCount
speichert die aktuelle Bewertung, und myMaxStarCount
speichert die höchstmögliche Bewertung (normalerweise 5).
Das Makro Q_DECLARE_METATYPE()
macht QVariant den Typ StarRating
bekannt, wodurch es möglich ist, StarRating
Werte in QVariant zu speichern.
Implementierung der StarRating-Klasse
Der Konstruktor initialisiert myStarCount
und myMaxStarCount
und richtet die Polygone ein, die zum Zeichnen von Sternen und Diamanten verwendet werden:
StarRating::StarRating(int starCount, int maxStarCount) : myStarCount(starCount), myMaxStarCount(maxStarCount) { starPolygon << QPointF(1.0, 0.5); for (int i = 1; i < 5; ++i) starPolygon << QPointF(0.5 + 0.5 * std::cos(0.8 * i * 3.14), 0.5 + 0.5 * std::sin(0.8 * i * 3.14)); diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4) << QPointF(0.6, 0.5) << QPointF(0.5, 0.6) << QPointF(0.4, 0.5); }
Die Funktion paint()
malt die Sterne in diesem StarRating
Objekt auf ein Malgerät:
void StarRating::paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(Qt::NoPen); painter->setBrush(mode == EditMode::Editable ? palette.highlight() : palette.windowText()); const int yOffset = (rect.height() - PaintingScaleFactor) / 2; painter->translate(rect.x(), rect.y() + yOffset); painter->scale(PaintingScaleFactor, PaintingScaleFactor); for (int i = 0; i < myMaxStarCount; ++i) { if (i < myStarCount) painter->drawPolygon(starPolygon, Qt::WindingFill); else if (mode == EditMode::Editable) painter->drawPolygon(diamondPolygon, Qt::WindingFill); painter->translate(1.0, 0.0); } painter->restore(); }
Zuerst werden der Stift und der Pinsel festgelegt, die zum Malen verwendet werden. Der Parameter mode
kann entweder Editable
oder ReadOnly
sein. Wenn mode
editierbar ist, verwenden wir die Farbe Highlight anstelle der Farbe WindowText, um die Sterne zu zeichnen.
Dann zeichnen wir die Sterne. Im Modus Edit
werden anstelle der Sterne Rauten gezeichnet, wenn die Bewertung niedriger ist als die höchste Bewertung.
Die Funktion sizeHint()
gibt die bevorzugte Größe für einen Bereich zurück, auf den die Sterne gemalt werden sollen:
Die bevorzugte Größe ist gerade groß genug, um die maximale Anzahl von Sternen zu malen. Die Funktion wird sowohl von StarDelegate::sizeHint()
als auch von StarEditor::sizeHint()
aufgerufen.
Die Funktion main()
Hier ist die Funktion main()
des Programms:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTableWidget tableWidget(4, 4); tableWidget.setItemDelegate(new StarDelegate); tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked); tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows); tableWidget.setHorizontalHeaderLabels({"Title", "Genre", "Artist", "Rating"}); populateTableWidget(&tableWidget); tableWidget.resizeColumnsToContents(); tableWidget.resize(500, 300); tableWidget.show(); return app.exec(); }
Die Funktion main()
erstellt ein QTableWidget und setzt ein StarDelegate
darauf. DoubleClicked und SelectedClicked werden als edit triggers gesetzt, so dass der Editor mit einem einzigen Klick geöffnet wird, wenn der Sternbewertungspunkt ausgewählt wird.
Die Funktion populateTableWidget()
füllt die QTableWidget mit Daten:
void populateTableWidget(QTableWidget *tableWidget) { static constexpr struct { const char *title; const char *genre; const char *artist; int rating; } staticData[] = { { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 }, ... { nullptr, nullptr, nullptr, 0 } }; for (int row = 0; staticData[row].title != nullptr; ++row) { QTableWidgetItem *item0 = new QTableWidgetItem(staticData[row].title); QTableWidgetItem *item1 = new QTableWidgetItem(staticData[row].genre); QTableWidgetItem *item2 = new QTableWidgetItem(staticData[row].artist); QTableWidgetItem *item3 = new QTableWidgetItem; item3->setData(0, QVariant::fromValue(StarRating(staticData[row].rating))); tableWidget->setItem(row, 0, item0); tableWidget->setItem(row, 1, item1); tableWidget->setItem(row, 2, item2); tableWidget->setItem(row, 3, item3); } }
Beachten Sie den Aufruf von QVariant::fromValue, um ein StarRating
in ein QVariant umzuwandeln.
Mögliche Erweiterungen und Vorschläge
Es gibt viele Möglichkeiten, das Model/View-Framework von Qt anzupassen. Der in diesem Beispiel verwendete Ansatz ist für die meisten benutzerdefinierten Delegaten und Editoren geeignet. Beispiele für Möglichkeiten, die vom Sterndelegaten und Sterneditor nicht genutzt werden, sind:
- Es ist möglich, Editoren programmatisch durch den Aufruf von QAbstractItemView::edit() zu öffnen, anstatt sich auf Edit-Trigger zu verlassen. Dies könnte verwendet werden, um andere Bearbeitungsauslöser zu unterstützen, als die, die vom QAbstractItemView::EditTrigger enum angeboten werden. Im Beispiel des Star Delegate könnte es zum Beispiel sinnvoll sein, mit der Maus über ein Element zu fahren, um einen Editor aufzurufen.
- Durch die Neuimplementierung von QAbstractItemDelegate::editorEvent() ist es möglich, den Editor direkt im Delegaten zu implementieren, anstatt eine separate QWidget Unterklasse zu erstellen.
© 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.