c++qtqt5qstyleditemdelegate

QStyledItemDelegate how to make two widgets in one row?


I'm created a QStyledItemDelegate class in which I want to make some item checkable and some with two widgets. But it is not working right. What am I missing? This is what it looks like:

enter image description here

See row 1, looks like the two widgets are there but they are not really showing. And I need some help to make item checkable (this is different than adding a checkbox?). Thank you.

Here is my QStyledItemDelegate class :

//! [0]
SpinBoxDelegate::SpinBoxDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{
}
//! [0]

//! [1]
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
    const QStyleOptionViewItem &option,
    const QModelIndex &index) const
{
    if (index.row()==1) {
    QLineEdit* lineBox;
    QCheckBox* checkBox;
    QWidget *panel;
    panel = new QWidget(parent);
    QHBoxLayout *layout = new QHBoxLayout;

    lineBox = new QLineEdit( );
    lineBox->setText("abc");
    checkBox = new QCheckBox( );

    layout->addWidget(checkBox);
    layout->addWidget(lineBox);
    panel->setLayout(layout);
    return panel;
}else if (index.row()==2) {
// need to make this check-able item?
}else{
    QLineEdit *editor = new QLineEdit(parent);
    return editor;
}
}
//! [1]

//! [2]
void SpinBoxDelegate::setEditorData(QWidget *editor,
                                const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();

if (index.row()==1) {
// need something here?
}else{
    QLineEdit *spinBox = static_cast<QLineEdit*>(editor);
    spinBox->setText("value");

}
}
//! [2]

//! [3]
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
if (index.row()==1) {
// need something here?
}else{
   QLineEdit *spinBox = static_cast<QLineEdit*>(editor);
   model->setData(index, spinBox->text(), Qt::EditRole);
}


}
//! [3]

//! [4]
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
    const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
//! [4]

this is my main.cpp:

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QStandardItemModel model(4, 2);
//QTableView tableView;
QTreeView treeView;
treeView.setModel(&model);

SpinBoxDelegate delegate;
treeView.setItemDelegate(&delegate);
//! [0]

//tableView.horizontalHeader()->setStretchLastSection(true);
treeView.setRootIsDecorated(false);
treeView.setHeaderHidden(true);
treeView.setIndentation(20);
//! [1]
for (int row = 0; row < 4; ++row) {
    for (int column = 0; column < 2; ++column) {
        QModelIndex index = model.index(row, column, QModelIndex());
        model.setData(index, QVariant((row + 1) * (column + 1)));
    }
//! [1] //! [2]
}
//! [2]

//! [3]
treeView.setWindowTitle(QObject::tr("Spin Box Delegate"));
treeView.show();
return app.exec();
}
//! [3]

This is eventually what I want to achieve:

enter image description here


Solution

  • To maintain an order you must create a class that implements the custom widget.

    On the other hand you must store the data in the roles since the editors can continuously be created and destroyed.

    To obtain the correct size, use the sizeHint() that is stored in a role.

    And finally so that you are always visible you must use the openPersistentEditor() method:

    #include <QApplication>
    #include <QHBoxLayout>
    #include <QLineEdit>
    #include <QCheckBox>
    #include <QStyledItemDelegate>
    #include <QStandardItemModel>
    #include <QTreeView>
    #include <QComboBox>
    #include <QHeaderView>
    
    enum CustomRoles{
        SelectRole = Qt::UserRole
    };
    
    class EditorWidget: public QWidget{
        Q_OBJECT
    public:
        EditorWidget(QWidget *parent=nullptr)
            : QWidget(parent),
              checkBox(new QCheckBox),
              lineBox(new QLineEdit),
              comboBox(new QComboBox)
        {
            QHBoxLayout *layout = new QHBoxLayout(this);
            comboBox->addItems({"item1", "item2", "item3"});
            layout->addWidget(checkBox);
            layout->addWidget(lineBox);
            layout->addWidget(comboBox);
        }
    
        int currentIndex(){
            return comboBox->currentIndex();
        }
        void setCurrentIndex(int index){
            comboBox->setCurrentIndex(index);
        }
        QString text() const{
            return  lineBox->text();
        }
        void setText(const QString &text){
            lineBox->setText(text);
        }
        Qt::CheckState checkState() const{
            return checkBox->checkState();
        }
        void setCheckState(Qt::CheckState state){
            checkBox->setCheckState(state);
        }
    private:
        QCheckBox *checkBox;
        QLineEdit *lineBox;
        QComboBox *comboBox;
    };
    
    class Delegate: public QStyledItemDelegate{
        Q_OBJECT
    public:
        using QStyledItemDelegate::QStyledItemDelegate;
        void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
            Q_UNUSED(painter)
            Q_UNUSED(option)
            Q_UNUSED(index)
        }
        QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
            Q_UNUSED(option)
            Q_UNUSED(index)
            EditorWidget *editor = new EditorWidget(parent);
            return editor;
        }
        void setEditorData(QWidget *editor, const QModelIndex &index) const{
            EditorWidget *widget = static_cast<EditorWidget*>(editor);
            widget->blockSignals(true);
            widget->setText(index.data(Qt::DisplayRole).toString());
            Qt::CheckState state = static_cast<Qt::CheckState>(index.data(Qt::CheckStateRole).toInt());
            widget->setCheckState(state);
            widget->setCurrentIndex(index.data(CustomRoles::SelectRole).toInt());
            widget->blockSignals(false);
        }
        void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const{
            EditorWidget *widget = static_cast<EditorWidget*>(editor);
            model->setData(index, widget->text(), Qt::DisplayRole);
            model->setData(index, widget->checkState(), Qt::CheckStateRole);
            model->setData(index, widget->sizeHint(), Qt::SizeHintRole);
            model->setData(index, widget->currentIndex(), CustomRoles::SelectRole);
        }
        void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const{
            Q_UNUSED(index)
            editor->setGeometry(option.rect);
        }
        QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
        {
            QSize s =  index.data(Qt::SizeHintRole).toSize();
            return s.isValid() ? s: QStyledItemDelegate::sizeHint(option, index);
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QTreeView treeView;
        QStandardItemModel model(4, 2);
        treeView.setModel(&model);
    
        Delegate delegate;
        treeView.setItemDelegate(&delegate);
    
        treeView.header()->setSectionResizeMode(QHeaderView::ResizeToContents);
        treeView.setRootIsDecorated(false);
        treeView.setHeaderHidden(true);
        treeView.setIndentation(20);
        for (int row = 0; row < 4; ++row) {
            for (int column = 0; column < 2; ++column) {
                QModelIndex index = model.index(row, column, QModelIndex());
                model.setData(index, QVariant((row + 1) * (column + 1)));
                treeView.openPersistentEditor(index);
            }
        }
        treeView.resize(treeView.sizeHint());
        treeView.show();
    
        return a.exec();
    }
    
    #include "main.moc"
    

    enter image description here

    The complete example can be found in the following link