2

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

YeP
  • 191
  • 5
  • 18
  • ok, added a screenshot... – YeP Jul 29 '18 at 06:16
  • ok, I added my main.cpp. I modified this from the qt example and just try to make my question. I tried to change the QTableView in example to QTreeView, doesn't seem to make a difference... QSpinbox has nothing to do with my question. – YeP Jul 29 '18 at 06:25
  • I added what I want to achieve, basically a checkable item with a LineEdit and a ComboBox following it... I'm just trying things out in an example for now. Sorry about the confusion. Thanks. – YeP Jul 29 '18 at 06:34
  • That works perfectly. Thanks. So if I have other items with different widget, I need to create different class for each kind? Also what is CustomRoles::SelectRole for? Thanks a lot. – YeP Jul 30 '18 at 02:58

1 Answers1

6

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • One additional question... In the treeView I have some root items and some child items under each root item. I try to assign something like EditorWidget to root items and nothing happens. What am I missing? Thanks. – YeP Aug 01 '18 at 08:12
  • @YeP I think you are assigning it incorrectly, without code it will be difficult to know where the problem is. – eyllanesc Aug 01 '18 at 08:14
  • I think the "treeView.openPersistentEditor(index);" in main is the thing. Your code loops through all items so all is showing right. For some reason in my code I did not do this for the root items. Is it that openPersistentEditor causing the item to be updated? Is it ok to openPersistentEditor on items which I don't want/use any editor? Thanks. – YeP Aug 03 '18 at 06:02