How to invoke QStyledItemDelegate::setModelData when using custom editors?
-
Hello everyone!
I'm using QPushButton as editor and I want to call QStyledItemDelegate::setModelData overrode method each time the button is clicked. So here is my code:
QWidget* CEditorDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const { //... connect(pButton, &QPushButton::clicked, this, &CEditorDelegate::CommitData); } void CEditorDelegate::CommitData() { QPushButton* editor = qobject_cast<QPushButton*>(sender()); if (!editor) return; emit commitData(editor); } void CEditorDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { //... model->setData(index, data, Qt::EditRole); }
But I see that
setModelData
is never called. What am I doing wrong?
I am also surprised that even here I saw an approach withstd::bind
inconnect
, so in my case that would beconnect(pButton, &QPushButton::clicked, this, std::bind(&QStyledItemDelegate::commitData, this, pButton));
But in fact it won't work, because
QStyledItemDelegate::createEditor
isconst
, so it looks like I am doing something wrong with my non-constCommitData
method even if it's called due to successful connection.My code is based on this topic, for example:
QStyledItemDelegate with Custom Widget: setModelData function never gets called -
B bibasmall deleted this topic on
-
B bibasmall restored this topic on
-
@JonB
In addition to my previous post —setModelData
is called with such connects, so it seems withsender()
I just abused a bug.connect(pButton, &QPushButton::clicked, this, std::bind(&CEditorDelegate::CommitData, const_cast<CEditorDelegate*>(this), pButton)); //or connect(pButton, &QPushButton::clicked, [nonConstThis = const_cast<CEditorDelegate*>(this), pButton]() { nonConstThis->CommitData(pButton); });
Actually I don't understand how do I customise editors behaviour more elegantly (considering
const_cast
a bad practise), since most of the QStyledItemDelegate methods are constant while signals are not. -
@bibasmall said in How to invoke QStyledItemDelegate::setModelData when using custom editors?:
But I see that
setModelData
is never called. What am I doing wrong?You use
QPushButton* editor = qobject_cast<QPushButton*>(sender());
. I would never usesender()
. Have you verified what that is returning? Ifsender()
isnullptr
or not aQPushButton*
you won'temit commitData(editor)
. -
@JonB
What would you use instead ofsender()
?
The other way I see here isconst_cast<CEditorDelegate*>(this)
inCEditorDelegate::createEditor
, what is really ugly.@JonB said in How to invoke QStyledItemDelegate::setModelData when using custom editors?:
Have you verified what that is returning?
I did, I see that
commitData
is actually emitted in debugger. -
The reason why the
setModelData
method is not being called is that you are not emitting thecommitData
signal from yourCommitData
method. ThecommitData
signal is what triggers thesetModelData
method in the delegate. Therefore, you need to emit this signal from yourCommitData
method.Here's how you can modify your
CommitData
method to emit thecommitData
signal:void CEditorDelegate::CommitData() { QPushButton* editor = qobject_cast<QPushButton*>(sender()); if (!editor) return; emit commitData(editor); // emit the commitData signal }
Regarding your use of
std::bind
in theconnect
statement, it is unnecessary and can be replaced with a lambda function that calls theCommitData
method directly. Theconst
qualifier on thecreateEditor
method is not relevant here, as you are not trying to modify the object's state within the method.Here's an example of how you can modify the
connect
statement to use a lambda function:connect(pButton, &QPushButton::clicked, this, [this, pButton]() { CommitData(); });
This lambda function captures
this
andpButton
by value, and calls theCommitData
method when the button is clicked. -
@bibasmall
sender()
became unnecessary when new styleconnect()
syntax allowing C++ lambdas for slots was introduced a decade ago.connect(pButton, &QPushButton::clicked, this, [this, pButton]() { CommitData(pButton); } ); void CEditorDelegate::CommitData(QPushButton *btn) { }
I'm not sure about why your
emit commitData(editor)
does not causesetModelData()
to be called. When I did editing delegates I did notemit commitData(editor)
and I did not have another widget like a button to conclude editing. I just overrodesetEditorData()
&setModelData()
, and it all just "worked". -
@James-Gallegos said in How to invoke QStyledItemDelegate::setModelData when using custom editors?:
is that you are not emitting the commitData signal from your CommitData method.
But the OP has always shown he does do that.
Here's how you can modify your CommitData method to emit the commitData signal:
void CEditorDelegate::CommitData() { QPushButton* editor = qobject_cast<QPushButton*>(sender()); if (!editor) return; emit commitData(editor); // emit the commitData signal }
This is precisely what the OP showed he currently has, character for character.
-
@JonB
The reason why I usedsender()
is on the screenshot — my code won't compile if I call the non-const method for constthis
.
Somehow it compiles when CommitData has no arguments and the connection looks like this (so that's why I get the pointer to the editor fromsender()
):connect(pButton, &QPushButton::clicked, this, &CEditorDelegate::CommitData);
Though I'm sure it's a bug and it shouldn't compile for the same qualifier reason.
I've used the same approach before to force QCombobox close right after selecting an option, so I emitted two signals:emit commitData(editor); emit closeEditor(editor);
And I had no problem with invoking
setModelData
. But for a buttonsetModelData
is just silently not called. I see that the signal is emitted after I click the button, but no reaction after. -
@JonB
In addition to my previous post —setModelData
is called with such connects, so it seems withsender()
I just abused a bug.connect(pButton, &QPushButton::clicked, this, std::bind(&CEditorDelegate::CommitData, const_cast<CEditorDelegate*>(this), pButton)); //or connect(pButton, &QPushButton::clicked, [nonConstThis = const_cast<CEditorDelegate*>(this), pButton]() { nonConstThis->CommitData(pButton); });
Actually I don't understand how do I customise editors behaviour more elegantly (considering
const_cast
a bad practise), since most of the QStyledItemDelegate methods are constant while signals are not. -
@bibasmall
I'm not understanding what is going on with yourconst
or not. The slotCommitData()
must not beconst
. Is it??Hmm, you mean you call it from
CEditorDelegate::createEditor()
, that isconst
, so itsthis
isconst
, and you can't then use that for the slot object? Then I really don't know why it's OK for theconnect()
to actual slot but not to lambda (I suspect it's this issue, not because the newCommitData(QPushButton *btn)
parameter....Is your
CommitData()
const? Doesn't look like it. If you made itconst
does that allow theconnect()
?UPDATE
I think this has crossed with your latest. I believe you show there you have found a way. I prefer the second one.Basically you have raised a "gotcha":
createEditor()
isconst
, and that prevents you from connecting a slot tothis
inside it, which is something one might well want to do. It (apparently) works when&CEditorDelegate::CommitData
is passed on its own as a standalone argument, but not if you try a lambda withthis->CommitData()
. If the C++ super-experts here see this they might comment.... -
B bibasmall has marked this topic as solved on