Custom QItemDelegate works in QTableView but not QTreeView



  • I wrote a custom QItemDelegate to put a QCheckBox in a column of a QTableView which worked quite well:

    #include <QCheckBox>
    #include <QHBoxLayout>
    #include "toolwidget/checkboxdelegate.h"
    
    CheckboxDelegate::CheckboxDelegate(QObject* parent) : QItemDelegate(parent)
    {
    }
    
    QWidget* CheckboxDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
    {
        Q_UNUSED(option)
        Q_UNUSED(index)
    
        // Create a new checkbox. Use a layout to align (center) it.
        QCheckBox* editor = new QCheckBox(parent);
        editor->setObjectName("CheckBox");
        QHBoxLayout* layout = new QHBoxLayout;
        layout->addStretch();
        layout->addWidget(editor);
        layout->addStretch();
        QWidget* editorWidget = new QWidget(parent);
        editorWidget->setLayout(layout);
    
        return editorWidget;
    }
    
    void CheckboxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
    {
        // Retrieve the checked state from the item
        bool checked = index.model()->data(index, Qt::EditRole).toBool();
    
        // Retrieve the checkbox
        QCheckBox* checkbox = static_cast<QCheckBox*>(editor->findChild<QCheckBox*>("CheckBox"));
        if (!checkbox) {
            qCritical("CheckboxDelegate::setEditorData(): Couldn't retrieve QCheckBox. Aborting.");
            return;
        }
    
        // Set the checkbox state
        checkbox->setChecked(checked);
    }
    
    void CheckboxDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
    {
        QCheckBox* checkbox = static_cast<QCheckBox*>(editor->findChild<QCheckBox*>("CheckBox"));
        if (!checkbox) {
            qCritical("CheckboxDelegate::setModelData(): Couldn't retrieve QCheckBox. Aborting.");
            return;
        }
    
        // Set the model data
        model->setData(index, checkbox->isChecked(), Qt::EditRole);
    }
    
    void CheckboxDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
    {
        Q_UNUSED(index)
    
        editor->setGeometry(option.rect);
    }
    
    void CheckboxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
    {
        // Retrieve the checked state from the item
        Qt::CheckState checkState = index.model()->data(index, Qt::DisplayRole).toBool() ? Qt::Checked : Qt::Unchecked;
    
        // Render
        drawCheck(painter, option, option.rect, checkState);
    }
    
    QSize CheckboxDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
    {
        Q_UNUSED(option)
        Q_UNUSED(index)
    
        QCheckBox checkbox;
    
        return checkbox.sizeHint();
    }
    

    Now I decided that I'd much rather use a QTreeView than a QTableView because I want to introduce hierarchies in my model. The problem is that my CheckboxDelegate doesn't work there anymore.
    I still see the checkbox being rendered but I can't change the state of it anymore. When I click on it I see that QAbstractItemModel::setData() is being called successfully but it always writes back the same value - this means that the checkbox never gets toggled.

    I'm a bit lost here. I don't know how much more information is required. Any ideas?


  • Lifetime Qt Champion

    Hi,

    Out of curiosity, why not return item flag Qt::ItemIsUserCheckable from your model ? IIRC it should provide you with what you need directly and avoid the need of a custom delegate.

    On a side note, you should use qobject_cast when dealing with QObject casting. static_cast doesn't do any validation.



  • I tried using Qt::ItemIsUserCheckable and it has the expected effect. However, instead of displaying a regular checkbox it displayed a combobox with the values "True" and "False". Hence the custom delegate.

    I replaced the static_cast with qobject_cast as per your recommendation. Of course, as expected, this didn't affect the problem that I'm having.

    Any ideas?


  • Lifetime Qt Champion

    You also need to modify your data function for the corresponding role.

    See the "extending the read only example with roles" part of the model view tutorial here.



  • I also modified my model. Actually, when I was still using the QTableView I followed exactly the example you linked and I got it working. The reason why I created my own custom delegate anyway is because I want one single checkbox that is centered in the cell. The "built-in" feature (as shown in the example you linked) adds a checkbox to the decoration of a cell.

    As mentioned everything works well in a QTableView. It's just that it doesn't work that well in a QTreeView.


  • Lifetime Qt Champion

    Can you setup a minimal project that shows the behavior ?



  • @SGaist

    Yup, here you go: <link_removed>
    I hope that helps... really lost here.

    I am using Qt 5.6.1 on Windows 10 64-Bit with MinGW 4.9.2.



  • At the end, @SGaist pointed me to this implementation of a checkbox item delegate which works very well.