Search code examples
c++qtcomboboxqtableviewqstyleditemdelegate

dataChanged signal does not work with ComboBoxDelegate


My problem is the following:

There is a QTableViewand a QStandardItemModel used in this way:

ui->tableView->setModel(model);
model->setItem(myrow, mycolumn, myQStandardItem);

and a comboboxdelegate:

ComboBoxDelegate* mydelegate = new ComboBoxDelegate();
ui->tableView->setItemDelegateForColumn(mycolumn,mydelegate);

Every time the value of a cell of the table is changed (by the combo-box) i need to catch the new value and the index of the cell just modified.I am using the signal dataChaged associated to the model in this way:

connect(model,SIGNAL(dataChanged(QModelIndex&,QModelIndex&)),this,SLOT(GetChangedValue(QModelIndex&)));

but it does not work, it never calls the method GetChangedValue though the combo-box has changed its value. Am i skipping any step?

Here below the code of the ComboBoxDelegate:

class ComboBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:

    ComboBoxDelegate(QVector<QString>& ItemsToCopy,QObject *parent = 0);
    ~ComboBoxDelegate();
     void setItemData(QVector<QString>& ItemsToCopy);

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const ;
    void setEditorData(QWidget *editor, const QModelIndex &index) const;
    void setModelData(QWidget *editor, QAbstractItemModel *model, const  QModelIndex &index) const;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;



    private:
     QVector<QString> Items;

};

ComboBoxDelegate::ComboBoxDelegate(QVector<QString>&  ItemsToCopy,QObject  *parent)
:QStyledItemDelegate(parent)
{
    setItemData(ItemsToCopy);
}


ComboBoxDelegate::~ComboBoxDelegate()
{
}


QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const     QStyleOptionViewItem &option, const QModelIndex &index) const
{

    QComboBox* editor = new QComboBox(parent);
    editor->setEditable(true);



    for (int i = 0; i < Items.size(); ++i)
    {
        editor->addItem(Items[i]);
    }


    editor->setStyleSheet("combobox-popup: 0;");

    return editor;
}




void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex  &index) const
{
    QComboBox *comboBox = static_cast<QComboBox*>(editor);
    QString currentText = index.data(Qt::EditRole).toString();
    int cbIndex = comboBox->findText(currentText);
    comboBox->setCurrentIndex(cbIndex);
}



void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel    *model, const QModelIndex &index) const
 {
    QComboBox *comboBox = static_cast<QComboBox*>(editor);
    model->setData(index, comboBox->currentText(), Qt::EditRole);

}



void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const   QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
    editor->setGeometry(option.rect);

}



void ComboBoxDelegate::setItemData(QVector<QString>&  ItemsToCopy)
{
    for (int row = 0; row < ItemsToCopy.size(); ++row)
    {

       Items.push_back(ItemsToCopy[row]);


   }

}


Solution

  • The problem with your delegate implementation is that you do not emit commitData signal when the combo index is changed. It's stated in the Qt documentation :

    This signal must be emitted when the editor widget has completed editing the data, and wants to write it back into the model.

    You can have the combo-box as a member of the delegate class and connect the currentIndexChanged signal of combo-box to some slot which emits commitData :

    #include <QItemDelegate>
    
    #include <QComboBox>
    
    class ComboBoxDelegate: public QItemDelegate
    {
     Q_OBJECT
    public:
        ComboBoxDelegate(QObject *parent = 0);
    
        QWidget *createEditor( QWidget *parent,
                                const QStyleOptionViewItem &option,
                                const QModelIndex &index ) const;
    
        void setEditorData( QWidget *editor,
                                const QModelIndex &index ) const;
    
        void setModelData( QWidget *editor,
                                QAbstractItemModel *model,
                                const QModelIndex &index ) const;
    
        void updateEditorGeometry( QWidget *editor,
                                const QStyleOptionViewItem &option,
                                const QModelIndex &index ) const;
    
        QStringList comboItems;
    
        mutable QComboBox *combo;
    
    private slots:
    
        void setData(int val);
    
    };
    
    ComboBoxDelegate::ComboBoxDelegate(QObject *parent ):QItemDelegate(parent)
    {
            comboItems<<"Item 1"<<"Item 2"<<"Item 3";
    }
    
    QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        combo = new QComboBox( parent );
        QObject::connect(combo,SIGNAL(currentIndexChanged(int)),this,SLOT(setData(int)));
        combo->addItems(comboItems);
        combo->setMaxVisibleItems(comboItems.count());
        return combo;
    }
    
    void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
        QString text = index.model()->data( index, Qt::DisplayRole ).toString();
    
        int comboIndex = comboItems.indexOf(QRegExp(text));
    
        if(comboIndex>=0)
            (static_cast<QComboBox*>( editor ))->setCurrentIndex(comboIndex);
    }
    
    void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        model->setData( index, static_cast<QComboBox*>( editor )->currentText() );
    }
    
    
    void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        editor->setGeometry( option.rect );
    }
    
    void ComboBoxDelegate::setData(int val)
    {
        emit commitData(combo);
        //emit closeEditor(combo);
    }
    

    As you see currentIndexChanged signal of combo-box is connected to setData slot which commits the data to the model. Also you should declare combo-box as mutable for newing it in createEditor which is constant. If a data member is declared mutable, then it is legal to assign a value to this data member from a const member function.

    Now the dataChanged signal would be emitted when the index of combo-box is changed.