QAbstractItemDelegate and editing
-
I wrote a class which based on QItemDelegate. It presents a combo box with some data. When I applying it on QTreeView or QTableView item i can change the value by double clicking on it and choosing the new value, than I must hit Enter key or click on any other item to change the value in item. If I close window, without hitting Enter key, or without clicking on other item, the value in item will not change. Is there any solution to solve this problem ? Here is the code of my delegate:
@class ComboBoxDelegate : public AItemDelegate { Q_OBJECT
//--------------------------------------------------------------------------------------------------
public: ComboBoxDelegate(QObject * parent = 0) : AItemDelegate(parent) {
}
public: ComboBoxDelegate(QObject * parent, DescriptionPtr description) : AItemDelegate(parent, description) {
}
//--------------------------------------------------------------------------------------------------
public: virtual ~ComboBoxDelegate() {
}
//--------------------------------------------------------------------------------------------------
public: virtual QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & /option/, const QModelIndex & /index/) const {
if (m_description == NULL)
return NULL;QComboBox * cb = new QComboBox(parent); const Choices & choices = m_description->getChoices(); int n = choices.getCount(); for (int i = 0; i < n; i++) { const Choice & item = choices.getItem(i); cb->addItem(item.getDisplayName(), item.getValue()); } return cb; }
//--------------------------------------------------------------------------------------------------
public: void setEditorData(QWidget * editor, const QModelIndex & index) const {
QComboBox * cb = static_cast<QComboBox *>(editor);
// QVariant value = index.data(Qt::EditRole);
QString s = index.data(Qt::DisplayRole).toString();
//int i = cb->findData(value);
int i = cb->findText(s);
// if(i == -1)
// cb->setCurrentIndex(0);
// else
cb->setCurrentIndex(i);}
//--------------------------------------------------------------------------------------------------
public: void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
QComboBox * cb = static_cast<QComboBox *>(editor);
int ndx = cb->currentIndex();
if (ndx >= 0) {
QVariant value = cb->itemData(ndx);
model->setData(index, value, Qt::EditRole);
}
}
//--------------------------------------------------------------------------------------------------
};@AItemDelegate:
@class AItemDelegate : public QItemDelegate {
//--------------------------------------------------------------------------------------------------
protected: DescriptionPtr m_description;
//--------------------------------------------------------------------------------------------------
public: AItemDelegate(QObject * parent) : QItemDelegate(parent) {
m_description = NULL;
}
//--------------------------------------------------------------------------------------------------
public: AItemDelegate(QObject * parent, DescriptionPtr description) : QItemDelegate(parent) {
m_description = description;
}
//--------------------------------------------------------------------------------------------------
public: virtual ~AItemDelegate() {
}
//--------------------------------------------------------------------------------------------------
public: DescriptionPtr getDescription() {
return m_description;
}
//--------------------------------------------------------------------------------------------------
public: void setDescription(DescriptionPtr description) {
m_description = description;
}
//--------------------------------------------------------------------------------------------------
public: virtual void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & /index/) const {
editor->setGeometry(option.rect);
}
//--------------------------------------------------------------------------------------------------
};@ -
Connect an appropriate signal of the widget containing your item view to a slot endEdit() in you item view subclass:
@
void MyItemViewSubclass::endEdit()
{
QModelIndex index = currentIndex();
currentChanged( index, index );
}
@This simulates clicking on an item and thus ends the editing process of the combobox.
You should call this slot too, if you are going to commit the data via some save button or the like. It works well and we use it all the time.
-
This delegate is using by more then one item View sub class. I can't even imagine how many times it will be used in project in feature and by how many people, because it not so small. So I need some local solution in my delegate or in AItemDelegate class. Is there any other ideas ?
-
The delegate does not catch widget state changes. So I see no better solution. You might create a central subclass of the needed item view/item widgets and implement endEdit there, to ease the use of the method.
-
Thanks for answer. So this will take a little bit more work than I expect.
-
And one more question: which signal I need to connect ? For example in QTreeView ? I can't find appropriate signal. Or maybe it must be my own signal emitted from one of delegate functions ?
-
I mean some signals or events from the containing widget (hide, close, etc.). maybe focusOut on the item view too. Also, if you have, for example, a save button, it is likely that it is connected to some slot, you should call endEdit there too.
-
There is one more simple solution that I find. In my delegate I need to add connect @ public: void setEditorData(QWidget * editor, const QModelIndex & index) const {
QComboBox * cb = static_cast<QComboBox *>(editor);
QString s = index.data(Qt::DisplayRole).toString();
int i = cb->findText(s);
cb->setCurrentIndex(i);connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); }@
And add a slot onCurrentIndexChanged : @public slots: void onCurrentIndexChanged(int index) {
QComboBox * cb = static_cast<QComboBox *>(sender());
emit commitData(cb);
emit closeEditor(cb);
}@
So it solved locally and works pretty well. -
Be aware that this will work with your combo boxes, but not with default generated line edits or the like!
-
I had the exact same issue and the currentChanged trick worked very nicely.
Thanks for the tip!
-
Just want to thanks for this trick, I modified it a little bit so it works with every QWidget editor
1- In createEditor, connect a custom signal in your QWidget Editor to a custom slot in the Delegate
@QWidget *IntervalDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
else if (index.column() == 3 ) {
CadenceEditor *editor = new CadenceEditor(parent);
connect (editor, SIGNAL(endEdit()), this, SLOT(closeWidgetEditor()) );
return editor;
}@2- close the editor in the custom delegate slot :
@void IntervalDelegate::closeWidgetEditor() {qDebug() << "CLOSE EDITOR NOW!"; QWidget *editor = qobject_cast<QWidget*>(sender()); emit commitData(editor); emit closeEditor(editor);
}@
-
I can now close the editor with a custom "OK" button, show in this image:
https://www.dropbox.com/s/95agb2c38f9p354/customEditors.pngProblem is when I click the OK button, the click get propaged under it and it open another editor, how can I stop the click? I already have setAttribute(Qt::WA_NoMousePropagation); on my custom QWidget editor..
Thanks
-
I think it is the default behavior in a QTableView, when a cell editing is completed, the editor automatically move to the adjacent(right) cell.
Kind of like excel.
I need to find how to turn off this feature now :)