Ejemplo de delegado estrella
El ejemplo de delegado en estrella muestra cómo crear un delegado que puede pintarse a sí mismo y que admite la edición.

Cuando se muestran datos en QListView, QTableView, o QTreeView, los elementos individuales son dibujados por un delegado. Además, cuando el usuario empieza a editar un elemento (por ejemplo, haciendo doble clic en él), el delegado proporciona un widget editor que se coloca encima del elemento mientras se realiza la edición.
Los delegados son subclases de QAbstractItemDelegate. Qt proporciona QStyledItemDelegate, que hereda de QAbstractItemDelegate y maneja los tipos de datos más comunes (en particular int y QString). Si necesitamos soportar tipos de datos personalizados, o queremos personalizar el renderizado o la edición para tipos de datos existentes, podemos subclasificar QAbstractItemDelegate o QStyledItemDelegate. Ver Clases Delegadas para más información sobre delegados, y Programación Modelo/Vista si necesitas una introducción de alto nivel a la arquitectura modelo/vista de Qt (incluyendo delegados).
En este ejemplo, veremos cómo implementar un delegado personalizado para renderizar y editar un tipo de dato "star rating", que puede almacenar valores como "1 de 5 estrellas".
El ejemplo consta de las siguientes clases:
StarRatinges el tipo de datos personalizado. Almacena una valoración expresada en estrellas, como "2 de 5 estrellas" o "5 de 6 estrellas".StarDelegatehereda QStyledItemDelegate y proporciona soporte paraStarRating(además de los tipos de datos ya manejados por QStyledItemDelegate).StarEditorhereda de QWidget y es utilizado porStarDelegatepara permitir al usuario editar una clasificación por estrellas utilizando el ratón.
Para mostrar el StarDelegate en acción, llenaremos un QTableWidget con algunos datos e instalaremos el delegado en él.
Definición de la clase StarDelegate
Esta es la definición de la clase 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(); };
Todas las funciones públicas son funciones virtuales reimplementadas de QStyledItemDelegate para proporcionar renderizado y edición personalizados.
Implementación de la Clase StarDelegate
La función paint() está reimplementada a partir de QStyledItemDelegate y se llama cada vez que la vista necesita repintar un elemento:
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); }
La función se invoca una vez por cada elemento, representado por un objeto QModelIndex del modelo. Si los datos almacenados en el elemento son un StarRating, lo pintamos nosotros mismos; de lo contrario, dejamos que QStyledItemDelegate lo pinte por nosotros. Esto garantiza que StarDelegate pueda manejar los tipos de datos más comunes.
Si el elemento es un StarRating, dibujamos el fondo si el elemento está seleccionado, y dibujamos el elemento utilizando StarRating::paint(), que revisaremos más adelante.
StartRatings se pueden almacenar en un QVariant gracias a la macro Q_DECLARE_METATYPE() que aparece en starrating.h. Más adelante hablaremos de ello.
La función createEditor() es llamada cuando el usuario comienza a editar un elemento:
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); }
Si el elemento es un StarRating, creamos un StarEditor y conectamos su señal editingFinished() a nuestra ranura commitAndCloseEditor(), para poder actualizar el modelo cuando se cierre el editor.
Esta es la implementación de commitAndCloseEditor():
void StarDelegate::commitAndCloseEditor() { StarEditor *editor = qobject_cast<StarEditor *>(sender()); emit commitData(editor); emit closeEditor(editor); }
Cuando el usuario termina de editar, emitimos commitData() y closeEditor() (ambas declaradas en QAbstractItemDelegate), para decirle al modelo que hay datos editados y para informarle a la vista que el editor ya no es necesario.
La función setEditorData() se llama cuando se crea un editor para inicializarlo con los datos del modelo:
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); } }
Simplemente llamamos a setStarRating() en el editor.
La función setModelData() se ejecuta para transferir los datos del editor al modelo una vez finalizada la edición:
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); } }
La función sizeHint() devuelve el tamaño preferido de un elemento:
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); }
Simplemente reenviamos la llamada a StarRating.
Definición de la clase StarEditor
La clase StarEditor se utilizó al implementar StarDelegate. Aquí está la definición de la clase:
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; };
La clase permite al usuario editar un StarRating moviendo el ratón sobre el editor. Emite la señal editingFinished() cuando el usuario hace clic en el editor.
Las funciones protegidas se reimplementan a partir de QWidget para manejar los eventos de ratón y pintura. La función privada starAtPosition() es una función de ayuda que devuelve el número de la estrella bajo el puntero del ratón.
Implementación de la clase StarEditor
Empecemos con el constructor:
StarEditor::StarEditor(QWidget *parent) : QWidget(parent) { setMouseTracking(true); setAutoFillBackground(true); }
Activamos mouse tracking en el widget para que podamos seguir el cursor incluso cuando el usuario no mantiene pulsado ningún botón del ratón. También activamos QWidget's auto-fill background característica para obtener un fondo opaco. (Sin la llamada, el fondo de la vista brillaría a través del editor).
La función paintEvent() está reimplementada a partir de QWidget:
void StarEditor::paintEvent(QPaintEvent *) { QPainter painter(this); myStarRating.paint(&painter, rect(), palette(), StarRating::EditMode::Editable); }
Simplemente llamamos a StarRating::paint() para dibujar las estrellas, igual que hicimos al implementar 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); }
En el manejador de eventos del ratón, llamamos a setStarCount() en el miembro de datos privado myStarRating para reflejar la posición actual del cursor, y llamamos a QWidget::update() para forzar un repintado.
void StarEditor::mouseReleaseEvent(QMouseEvent *event) { emit editingFinished(); QWidget::mouseReleaseEvent(event); }
Cuando el usuario suelta el botón del ratón, simplemente emitimos la señal editingFinished().
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; }
La función starAtPosition() utiliza álgebra lineal básica para averiguar qué estrella está bajo el cursor.
Definición de la clase 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)
La clase StarRating representa una valoración como un número de estrellas. Además de contener los datos, también es capaz de pintar las estrellas en un QPaintDevice, que en este ejemplo es una vista o un editor. La variable miembro myStarCount almacena la valoración actual, y myMaxStarCount almacena la valoración más alta posible (normalmente 5).
La macro Q_DECLARE_METATYPE() hace que el tipo StarRating sea conocido por QVariant, haciendo posible almacenar valores de StarRating en QVariant.
Implementación de la clase StarRating
El constructor inicializa myStarCount y myMaxStarCount, y configura los polígonos utilizados para dibujar estrellas y diamantes:
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); }
La función paint() pinta las estrellas de este objeto StarRating en un dispositivo de pintura:
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(); }
Primero establecemos la pluma y el pincel que usaremos para pintar. El parámetro mode puede ser Editable o ReadOnly. Si mode es editable, usamos el color Highlight en lugar del WindowText para dibujar las estrellas.
Entonces dibujamos las estrellas. Si estamos en modo Edit, pintamos rombos en lugar de estrellas si la valoración es inferior a la valoración más alta.
La función sizeHint() devuelve el tamaño preferido de un área para pintar las estrellas:
El tamaño preferido es el justo para pintar el máximo número de estrellas. La función es llamada tanto por StarDelegate::sizeHint() como por StarEditor::sizeHint().
La función main()
Esta es la función main() del programa:
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(); }
La función main() crea un QTableWidget y establece un StarDelegate en él. DoubleClicked y SelectedClicked se establecen como edit triggers, de modo que el editor se abre con un solo clic cuando se selecciona el elemento de valoración de estrellas.
La función populateTableWidget() rellena el QTableWidget con datos:
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); } }
Observe la llamada a QVariant::fromValue para convertir un StarRating en un QVariant.
Posibles extensiones y sugerencias
Hay muchas formas de personalizar el marco modelo/vista de Qt. El enfoque utilizado en este ejemplo es apropiado para la mayoría de los delegados y editores personalizados. Ejemplos de posibilidades no utilizadas por el delegado estrella y el editor estrella son:
- Es posible abrir editores mediante programación llamando a QAbstractItemView::edit(), en lugar de depender de activadores de edición. Esto podría usarse para soportar otros disparadores de edición que los ofrecidos por el enum QAbstractItemView::EditTrigger. Por ejemplo, en el ejemplo del Delegado Estrella, pasar el ratón por encima de un elemento podría tener sentido para abrir un editor.
- Reimplementando QAbstractItemDelegate::editorEvent(), es posible implementar el editor directamente en el delegado, en lugar de crear una subclase QWidget aparte.
© 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.