Com\’è dura la professione!

11 aprile 2011

Delegate a delegate in a Qt View

Filed under: QT — lbell @ 20:59

The data presentation in a Qt Item View like a TreeView or a TableView is made by means of components that implement a QAbstractItemDelegate interface.

Write a custom delegate to gain full control on how the data are rendered forces us to face all the subtle details and intricacy of the measuring and styling of graphics items with the risk of producing a component that does not fit well with the style used in the host system. Moreover, speaking in a C++ term, we don’t know what is the base class that a particular view is using, so we can’t subclassing it to alter its behavior.

If our needs are limited, for example, to display a decoration over a read only standard item, a simple solution can be to use the existing view delegate to do the hard work, then render the decoration over the painted surface.

We can create a delegate deriving from QAbstractItemDelegate class, inquire the view that we want to manipulate about its actual delegate and store it as an internal variable of the new delegate. Our class can act as a bridge, implementing all the interface functions and the slots of a delegate and redirect incoming calls to the old delegate.

Our class will look like this (some code omitted for clarity)

class ElementItemDelegate : public QAbstractItemDelegate
{
    Q_OBJECT
    QAbstractItemDelegate *baseDelegate;
public:
    explicit ElementItemDelegate(QAbstractItemDelegate *newBaseDelegate, QObject *parent = 0);
    virtual ~ElementItemDelegate();
    virtual QWidget * createEditor () const;
    virtual bool editorEvent ( );
    virtual void paint ( ... ) const;
...
};

At the window creation we can retrieve the actual delegate from e. g. a tree widget and pass it to our delegate:

void MainWindow::setUpDelegates()
{
    QAbstractItemDelegate *oldDelegate = treeWidget->itemDelegate();
    ...
    ElementItemDelegate *newDelegate = new ElementItemDelegate( oldDelegate, this );
    ...
    treeWidget->setItemDelegateForColumn(0, newDelegate);
}

Each incoming call to our delegate is then redirected to the existing delegate.
The only method we are interested about is the paint() one. We let the old delegate do the hard work, then we paint:

void ElementItemDelegate::paint ( QPainter *painter, const QStyleOptionViewItem & option, 
   const QModelIndex & index ) const
{
    QStyleOptionViewItemV4 option4(option);
    // call the default delegate
    baseDelegate->paint(painter, option, index );
    Element * element = Element::fromModelIndex(index);
    if( NULL != element ) {
        QRect markRect = option.rect;
        if( option4.viewItemPosition == QStyleOptionViewItemV4::Beginning ) {
            markRect.setLeft(0);
        }
        markRect.setWidth(WIDTH_BAND);
        if( element->saved() ) {
            painter->fillRect(markRect, savedBrush);
        } else if ( element->edited() ) {
            painter->fillRect(markRect, editedBrush);
        } else {
            painter->fillRect(markRect, normalBrush);
        }
    }
}

Blog su WordPress.com.