![]() |
Home · Examples |
Code:
The Star Delegate example shows how to create a delegate that can paint itself and that supports edition.Delegates are subclasses of QAbstractItemDelegate. Qt Jambi provides QItemDelegate, which inherits QAbstractItemDelegate and handles the most common data types (notably Integer and String). If we need to support custom data types, or want to customize the rendering or the editing for existing data types, we can subclass QAbstractItemDelegate or QItemDelegate. See Delegate Classes for more information about delegates, and Model/View Programming if you need a high-level introduction to Qt Jambi's model/view architecture (including delegates).
In this example, we will see how to implement a custom delegate to render and edit a "star rating" data type, which can stores values such as "1 out of 5 stars".
The example consists of the following classes:
We provide the class with a constructor that takes a QWidget, which we use for instantiating the superclass. The native Qt code demands that all objects that inherit QObject has a parent; otherwise the program will fail.
The paint() method is reimplemented from QItemDelegate and is called whenever the view needs to repaint an item:
public void paint(QPainter painter, QStyleOptionViewItem option, QModelIndex index) { Object data = index.data(); if (data != null && data instanceof StarRating) { if (option.state().isSet(QStyle.StateFlag.State_Selected)) { painter.fillRect(option.rect(), option.palette().highlight()); } ((StarRating) data).paint(painter, option.rect(), option.palette(), StarRating.ReadOnly); } else super.paint(painter, option, index); }The method is invoked once for each item, represented by a QModelIndex object from the model. If the data stored in the item is a StarRating, we paint it ourselves; otherwise, we let QItemDelegate paint it for us. This ensures that the StarDelegate can handle the most common data types.
In the case where the item is a StarRating, we draw the background if the item is selected, and we draw the item using StarRating.paint(), which we will review later.
Any kind of Object can be stored in a model, but when the item delegate encounters items it does not know how to paint, it will leave the view for that item empty.
The createEditor() method is called when the user starts editing an item:
public QWidget createEditor(QWidget parent, QStyleOptionViewItem item, QModelIndex index) { Object data = index.data(); if (data instanceof StarRating) return new StarEditor(parent, (StarRating) data); else return super.createEditor(parent, item, index); }If the item is a StarRating, we create a StarEditor. The editor must have the parent; if not, it would be displayed as a top-level window.
The setEditorData() method is called when an editor is created to initialize it with data from the model:
public void setEditorData(QWidget editor, QModelIndex index) { Object data = index.data(); if (data instanceof StarRating) ((StarEditor) editor).setStarRating((StarRating) data); else super.setEditorData(editor, index); }We simply call setStarRating() on the editor.
The setModelData() method is called when editing is finished, to commit data from the editor to the model:
public void setModelData(QWidget editor, QAbstractItemModel model, QModelIndex index) { if (index.data() instanceof StarRating) model.setData(index, ((StarEditor) editor).starRating()); else super.setModelData(editor, model, index); }The editing is finished when the user clicks on the editor. Since we propagate the mouse click event (i.e., do not accept it), the view will close the editor for us when it receives the QMouseEvent.
The sizeHint() method returns an item's preferred size:
public QSize sizeHint(QStyleOptionViewItem option, QModelIndex index) { Object data = index.data(); if (data instanceof StarRating) return ((StarRating) data).sizeHint(); else return super.sizeHint(option, index); }We simply forward the call to StarRating.
The protected methods are reimplemented from QWidget to handle mouse and paint events. The private method starAtPosition() is a helper method that returns the number of the star under the mouse pointer.
Let's start with the constructor:
public StarEditor(QWidget parent, StarRating rating) { super(parent); starRating = rating; setMouseTracking(true); setAutoFillBackground(true); }We enable mouse tracking on the widget so we can follow the cursor even when the user doesn't hold down any mouse button. We also turn on QWidget's auto-fill background feature to obtain an opaque background. (Without the call, the view's background would shine through the editor.)
The paintEvent() method is reimplemented from QWidget:
public void paintEvent(QPaintEvent event) { QPainter painter = new QPainter(this); starRating.paint(painter, rect(), palette(), StarRating.ReadWrite); }We simply call StarRating.paint() to draw the stars, just like we did when implementing StarDelegate.
public void mouseMoveEvent(QMouseEvent event) { int star = starAtPosition(event.x()); if (star != starRating.getRating() && star > 0) { starRating.setRating(star); update(); } }In the mouse event handler, we call setRating() on the private data member starCount to reflect the current cursor position, and we call QWidget.update() to force a repaint.
public int starAtPosition(int x) { int star = (x / (starRating.sizeHint().width() / starRating.getMaxRating())) + 1; if (star <= 0 || star > starRating.getMaxRating()) return -1; return star; }The starAtPosition() method uses basic linear algebra to find out which star is under the cursor.
The constructor initializes starCount and maxStarCount:
public StarRating(int rating, int maxRating) { setupPolygons(); maxCount = maxRating; setRating(rating); }The paint() method paints the stars in this StarRating object on a paint device:
public void paint(QPainter painter, QRect rect, QPalette palette, int mode) { painter.save(); painter.setRenderHint(QPainter.RenderHint.Antialiasing, true); painter.setPen(Qt.PenStyle.NoPen); if (mode == ReadWrite) painter.setBrush(palette.highlight()); else painter.setBrush(palette.text()); int yOffset = (rect.height() - PaintingFactor) / 2; painter.translate(rect.x(), rect.y() + yOffset); painter.scale(PaintingFactor, PaintingFactor); for (int i = 0; i < maxCount; i++) { if (i < starCount) painter.drawPolygon(starPolygon, Qt.FillRule.WindingFill); else painter.drawPolygon(diamondPolygon, Qt.FillRule.WindingFill); painter.translate(1.0, 0.0); } painter.restore(); }We first set the pen and brush we will use for painting. The mode parameter can be either ReadWrite or ReadOnly. If mode is read and write, we use the Highlight color instead of the Foreground color to draw the stars.
Then we draw the stars. If we are in ReadWrite mode, we paint diamonds in place of stars if the rating is less than the highest rating.
The sizeHint() method returns the preferred size for an area to paint the stars on:
public QSize sizeHint(QStyleOptionViewItem option, QModelIndex index) { Object data = index.data(); if (data instanceof StarRating) return ((StarRating) data).sizeHint(); else return super.sizeHint(option, index); }The preferred size is just enough to paint the maximum number of stars. The method is called by both StarDelegate.sizeHint() and StarEditor.sizeHint().
The table is set up in the createTable() method:
public void createTable() { LinkedList<String> headers = new LinkedList<String>(); table = new QTableWidget(4, 4); table.setItemDelegate(new Delegate(table)); table.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked, QAbstractItemView.EditTrigger.SelectedClicked); table.setSelectionBehavior( QAbstractItemView.SelectionBehavior.SelectRows);The createTable() method creates a QTableWidget and sets a StarDelegate on it. We set...
}The rest of the method fills the table with data including star ratings. DoubleClicked and SelectedClicked are set as edit triggers, so that the editor is opened by a single click when the star rating item is selected.
Copyright © 2008 Trolltech | Trademarks | Qt Jambi 4.4.2_01 |