c++qtqt5qcomboboxqitemdelegate

QStyledItemDelegate: commit QComboBox value to model on click


I am setting a QStyledItemDelegate on my model for a particular field, and returning a QComboBox from QStyledItemDelegate::createEditor

QComboBox* createEditor(QWidget* parent)
{
    QComboBox* cb = new QComboBox(parent);

    cb->addItem("UNDEFINED");
    cb->addItem("TEST");
    cb->addItem("OSE");
    cb->addItem("TSE");

    return cb;
}

void setEditorData(QWidget* editor, const QModelIndex& index)
{
    QComboBox* cb = qobject_cast<QComboBox*>(editor);
    if (!cb)
        throw std::logic_error("editor is not a combo box");

    QString value = index.data(Qt::EditRole).toString();
    int idx = cb->findText(value);
    if (idx >= 0)
        cb->setCurrentIndex(idx);

    cb->showPopup();
}

This is working fine, and when I select the field in question I am shown a combo box.

combobox open

When I select an option from the drop-down list, the combobox closes and the item is displayed with a drop-down icon next to it:

selected

At this point I would like the QStyledItemDelegate::setModelData function to be called, so that selecting an item in the list commits the data to the model.

However, I am required to first press Enter to commit the data (whereby the drop-down icon disappears)

committed

void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index)
{
    QComboBox* cb = qobject_cast<QComboBox*>(editor);
    if (!cb)
        throw std::logic_error("editor is not a combo box");

    model->setData(index, cb->currentText(), Qt::EditRole);
}

Question:

How can I configure my QComboBox to automatically commit the data when the user selects an item in the list and the combobox list closes, rather than requiring the additional press of Enter?


Solution

  • You have to issue the signal commitData and closeEditor when an item is selected as shown in the following example:

    #include <QApplication>
    #include <QStandardItemModel>
    #include <QListView>
    #include <QStyledItemDelegate>
    #include <QComboBox>
    
    class ComboBoxDelegate: public QStyledItemDelegate{
    public:
        using QStyledItemDelegate::QStyledItemDelegate;
        QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
            Q_UNUSED(option)
            Q_UNUSED(index)
    
            QComboBox* editor = new QComboBox(parent);
            connect(editor,  QOverload<int>::of(&QComboBox::activated),
                    this, &ComboBoxDelegate::commitAndCloseEditor);
            editor->addItems({"UNDEFINED", "TEST", "OSE", "TSE"});
    
            return editor;
        }
        void setEditorData(QWidget *editor, const QModelIndex &index) const{
            QComboBox* cb = qobject_cast<QComboBox*>(editor);
            if (!cb)
                throw std::logic_error("editor is not a combo box");
    
            QString value = index.data(Qt::EditRole).toString();
            int idx = cb->findText(value);
            if (idx >= 0)
                cb->setCurrentIndex(idx);
            cb->showPopup();
        }
    private:
        void commitAndCloseEditor(){
            QComboBox *editor = qobject_cast<QComboBox *>(sender());
            emit commitData(editor);
            emit closeEditor(editor);
        }
    };
    
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QListView view;
    
        QStandardItemModel model;
    
        for(int i=0; i<10; i++){
            model.appendRow(new QStandardItem("UNDEFINED"));
        }
        view.setItemDelegate(new ComboBoxDelegate(&view));
        view.setModel(&model);
        view.show();
        return a.exec();
    }