Auf dieser Seite

Beispiel für einen „Star Delegate“

Das Beispiel „Star Delegate“ zeigt, wie man einen Delegaten erstellt, der sich selbst zeichnen kann und die Bearbeitung unterstützt.

Das Beispiel des „Star Delegate“

Bei der Anzeige von Daten in einem „ QListView “, „ QTableView “ oder „ QTreeView “ werden die einzelnen Elemente von einem Delegaten gezeichnet. Wenn der Benutzer zudem mit der Bearbeitung eines Elements beginnt (beispielsweise 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 gängigsten Datentypen verarbeitet (insbesondere ` int ` und ` QString`). Wenn wir benutzerdefinierte Datentypen unterstützen müssen oder die Darstellung bzw. Bearbeitung bestehender Datentypen anpassen möchten, können wir Unterklassen von ` QAbstractItemDelegate ` oder ` QStyledItemDelegate` erstellen. Weitere Informationen zu Delegaten finden Sie unter „Delegate Classes“; eine allgemeine Einführung in die Model-View-Architektur von Qt XML (einschließlich Delegaten) finden Sie unter „Model/View Programming “.

In diesem Beispiel sehen wir uns an, wie man einen benutzerdefinierten Delegaten implementiert, um einen Datentyp „Sternbewertung“ darzustellen 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 als Sterne ausgedrückte Bewertung, wie beispielsweise „2 von 5 Sternen“ oder „5 von 6 Sternen“.
  • StarDelegate erbt von ` QStyledItemDelegate ` und bietet Unterstützung für ` StarRating ` (zusätzlich zu den Datentypen, die bereits von ` QStyledItemDelegate` verarbeitet werden).
  • StarEditor erbt von ` QWidget ` und wird von ` StarDelegate ` verwendet, um dem Benutzer die Bearbeitung einer Sternebewertung mit der Maus zu ermöglichen.

Um die Klasse „ StarDelegate “ in Aktion zu zeigen, füllen wir ein „ QTableWidget “ mit einigen Daten und weisen ihm den Delegaten zu.

Klassendefinition von „StarDelegate“

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 neu implementierte virtuelle Funktionen aus „ QStyledItemDelegate “, um eine benutzerdefinierte Darstellung und Bearbeitung zu ermöglichen.

Implementierung der Klasse „StarDelegate“

Die Funktion ` paint()` wurde aus ` QStyledItemDelegate ` neu implementiert 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 einmal für jedes Element aufgerufen, das durch ein ` QModelIndex `-Objekt aus dem Modell repräsentiert wird. Wenn die im Element gespeicherten Daten vom Typ ` StarRating` sind, zeichnen wir sie selbst; andernfalls lassen wir ` QStyledItemDelegate ` dies für uns übernehmen. Dadurch wird sichergestellt, dass der ` 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 zeichnen das Element mithilfe von „ StarRating::paint() “, worauf wir später noch eingehen werden.

StartRatings können dank des Makros „ Q_DECLARE_METATYPE()“, das in „ starrating.h “ vorkommt, in einem „ QVariant “ gespeichert werden. Mehr dazu später.

Die Funktion „ createEditor()“ wird aufgerufen, wenn der Benutzer mit der Bearbeitung eines Elements 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);
}

Ist das Element ein „ StarRating “, erstellen wir ein „ StarEditor “ und verbinden dessen Signal „ editingFinished() “ mit unserem Slot „ commitAndCloseEditor() “, damit 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 die Bearbeitung abgeschlossen hat, senden wir commitData() und closeEditor() (beide in QAbstractItemDelegate deklariert), um dem Modell mitzuteilen, dass bearbeitete Daten vorliegen, und um der Ansicht mitzuteilen, dass der Editor nicht mehr benötigt wird.

Die Funktion ` setEditorData()` wird beim Erstellen eines Editors aufgerufen, um diesen 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 Daten aus dem Editor nach Abschluss der Bearbeitung in das Modell zu übertragen:

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.

Definition der Klasse „StarEditor“

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 “-Element zu bearbeiten, indem er die Maus über den Editor bewegt. Sie sendet das Signal „ editingFinished() “, wenn der Benutzer auf den Editor klickt.

Die geschützten Funktionen wurden aus „ QWidget “ neu implementiert, um Maus- und Zeichenereignisse zu verarbeiten. Die private Funktion „ starAtPosition() “ ist eine Hilfsfunktion, die die Nummer des Sterns unter dem Mauszeiger zurückgibt.

Implementierung der Klasse `StarEditor`

Beginnen wir mit dem Konstruktor:

StarEditor::StarEditor(QWidget *parent)
    : QWidget(parent)
{
    setMouseTracking(true);
    setAutoFillBackground(true);
}

Wir aktivieren „ mouse tracking “ für das Widget, damit wir dem Cursor auch dann folgen können, wenn der Benutzer keine Maustaste gedrückt hält. Außerdem aktivieren wir die Funktion „ auto-fill background “ von „ QWidget “, um einen undurchsichtigen Hintergrund zu erhalten. (Ohne diesen Aufruf würde der Hintergrund der Ansicht durch den Editor hindurchscheinen.)

Die Funktion ` paintEvent()` wird aus ` 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 bei der Implementierung von ` StarDelegate`.

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);
}

Im Mausereignis-Handler rufen wir ` setStarCount() ` für das private Datenelement ` myStarRating ` auf, um die aktuelle Cursorposition widerzuspiegeln, und wir rufen ` QWidget::update()` auf, um eine Neuzeichnung zu erzwingen.

void StarEditor::mouseReleaseEvent(QMouseEvent *event)
{
    emit editingFinished();
    QWidget::mouseReleaseEvent(event);
}

Wenn der Benutzer eine Maustaste loslässt, senden wir einfach das Signal „ editingFinished() “ aus.

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() ` nutzt grundlegende lineare Algebra, um herauszufinden, welcher Stern sich unter dem Cursor befindet.

Definition der Klasse „StarRating“

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 Anzahl von Sternen dar. Sie speichert nicht nur die Daten, sondern kann die Sterne auch auf einem „ QPaintDevice “ zeichnen, bei dem es sich in diesem Beispiel entweder um eine Ansicht oder einen Editor handelt. Die Membervariable „ myStarCount “ speichert die aktuelle Bewertung, und „ myMaxStarCount “ speichert die höchstmögliche Bewertung (in der Regel 5).

Das Makro „ Q_DECLARE_METATYPE() “ macht den Typ „ StarRating “ für „ QVariant “ bekannt, wodurch es möglich wird, „ StarRating “-Werte in „ QVariant “ zu speichern.

Implementierung der Klasse „StarRating“

Der Konstruktor initialisiert ` myStarCount ` und ` myMaxStarCount` und richtet die Polygone ein, die zum Zeichnen von Sternen und Rauten 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() “ zeichnet die Sterne in diesem „ StarRating “-Objekt auf ein Zeichenobjekt:

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();
}

Zunächst legen wir den Stift und den Pinsel fest, die wir zum Zeichnen verwenden werden. Der Parameter „ mode “ kann entweder auf „ Editable “ oder auf „ ReadOnly “ gesetzt werden. Wenn „ mode “ bearbeitbar ist, verwenden wir die Farbe „ Highlight “ anstelle der Farbe „ WindowText “, um die Sterne zu zeichnen.

Anschließend zeichnen wir die Sterne. Befinden wir uns im Modus „ Edit “, zeichnen wir Rauten anstelle von Sternen, wenn die Bewertung unter der höchsten Bewertung liegt.

Die Funktion „ sizeHint() “ gibt die bevorzugte Größe für den Bereich zurück, auf dem die Sterne gezeichnet werden sollen:

QSize StarRating::sizeHint() const
{
    return PaintingScaleFactor * QSize(myMaxStarCount, 1);
}

Die bevorzugte Größe reicht gerade aus, um die maximale Anzahl an Sternen zu zeichnen. 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 weist diesem ein „ StarDelegate “ zu. „ DoubleClicked “ und „ SelectedClicked “ werden auf „ edit triggers “ gesetzt, sodass der Editor mit einem einzigen Klick geöffnet wird, wenn das Element „Sternbewertung“ ausgewählt wird.

Die Funktion „ populateTableWidget() “ füllt das „ 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 Modell-Ansicht-Framework von Qt anzupassen. Der in diesem Beispiel verwendete Ansatz eignet sich für die meisten benutzerdefinierten Delegaten und Editoren. Beispiele für Möglichkeiten, die vom „star“-Delegaten und „star“-Editor nicht genutzt werden, sind:

  • Es ist möglich, Editoren programmgesteuert durch den Aufruf von ` QAbstractItemView::edit()` zu öffnen, anstatt sich auf Bearbeitungsauslöser zu verlassen. Dies könnte genutzt werden, um andere Bearbeitungsauslöser als die von der Enumeration ` QAbstractItemView::EditTrigger ` angebotenen zu unterstützen. Im Beispiel des „Star-Delegaten“ könnte es beispielsweise sinnvoll sein, einen Editor durch das Bewegen des Mauszeigers über ein Element aufzurufen.
  • Durch die Neuimplementierung von ` QAbstractItemDelegate::editorEvent()` ist es möglich, den Editor direkt im Delegaten zu implementieren, anstatt eine separate Unterklasse von ` QWidget ` zu erstellen.

Beispielprojekt @ code.qt.io

© 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.