I'm using a QTableWidget
and need some custom handling of the editing, so I set a QStyledItemDelegate
on it.
When the user finishes editing, the signal closeEditor()
is emitted, which I connect to to process the entered data. This signal is both emitted when Enter/Return is pressed, and when the user clicks somewhere else (outside the QTableWidgetItem
to be edited).
My question is: Is is possible to differntiate if the user presses Return/Enter or clicks somewhere else? I'd like to handle the "click outside" just like an ESC press (no data is changed and the original value of the QTableWidgetItem
is restored). By now, both cases change the data.
QAbstractItemDelegate::EndEditHint
(which is emitted with closeEditor()
) does not give me that information.
Thanks for all help!
Edit:
To process data before it's written back to the model, one can implement setModelData()
, but still, there seems to be no way if this function was called by pressing Enter/Return or by clicking somewhere else …
I recently made a sample to become (more) familiar with QStyledItemDelegate
concerning inline editing of cells:
In this sample, I did the update of underlying data model overloading QStyledItemDelegate::setModelData()
.
After having read this question, I just tested what happens if input is finished (or strictly speaking: aborted) by ESC. This is what I observed:
The underlying data is kept unchanged.
Digging deeper (i.e. setting a break-point into my overloaded setModelData()
), I observed setModelData()
is even not called in this case.
May be, the OPs issue can be solved easily by a little re-design of his delegate.
Finally, my derived class (declaration) I used to achieve a "full-featured" delegate to edit table cells inline:
class ValueNameDelegate: public QStyledItemDelegate {
// methods:
public:
/// @name Construction & Destruction
//@{
/// constructor.
ValueNameDelegate();
/// destructor.
virtual ~ValueNameDelegate() = default;
// disabled:
ValueNameDelegate(const ValueNameDelegate&) = delete;
ValueNameDelegate& operator=(const ValueNameDelegate&) = delete;
//@}
protected:
/// @name Overloaded Event Handlers
//@{
// inserts editor in table (by setParent(pQParent)) and
// returns the editor widget to edit cell.
virtual QWidget* createEditor(
QWidget *pQParent, const QStyleOptionViewItem &qOption,
const QModelIndex &qMIndex) const override;
// removes editor from table (by setParent(nullptr)).
virtual void destroyEditor(
QWidget *pQEditor, const QModelIndex &qMIndex) const override;
// reads data from table model and updates editor.
virtual void setEditorData(
QWidget *pQEditor, const QModelIndex &qMIndex) const override;
// reads data from editor and updates table model.
virtual void setModelData(
QWidget *pQEditor, QAbstractItemModel *pQModel,
const QModelIndex &qMIndex) const override;
//@}
};
Note:
In my case, there is only one editor widget created before-hand. It is re-used for every cell editing (instead of creating for each edit a new one).
This is a little bit different from the Qt Spin Box Delegate Example but works as expected.
I cannot imagine how this shouldn't make any difference concerning the OPs problem.
According to feed-back, OP wants to handle the focus-lose event like abort of input. This is in opposition how this is handled by default in QLineEdit
.
So, my solution is to provide an overloaded version of QLineEdit
. Instead of changing the behavior of QLineEdit
, it simply tracks in a member bool _confirmed
whether Enter was pressed. The most tricky part was to identify another suitable event or signal to reset the member. Finally, I decided that focusOutEvent()
is just called at the right time and hence suitable for this task.
testQLineEdit-Finished.cc
:
#include <QtWidgets>
class LineEdit: public QLineEdit {
private:
// flag: true ... last finished editing was confirmed
bool _confirmed;
public:
// Construction & Destruction
explicit LineEdit(
const QString &contents = QString(), QWidget *pQParent = nullptr):
QLineEdit(contents, pQParent),
_confirmed(false)
{
QObject::connect(this, &QLineEdit::returnPressed,
[this](){ onSigReturnPressed(); });
}
LineEdit(QWidget *pQParent): LineEdit(QString(), pQParent) { }
virtual ~LineEdit() = default;
LineEdit(const LineEdit&) = delete;
LineEdit& operator=(const LineEdit&) = delete;
public:
// returns whether last finished editing was confirmed.
bool isConfirmed() { return _confirmed; }
protected:
virtual void focusOutEvent(QFocusEvent *pQEvent) override
{
_confirmed = false;
QLineEdit::focusOutEvent(pQEvent);
}
private:
void onSigReturnPressed() { _confirmed = true; }
};
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
LineEdit qEdit(QString::fromUtf8("Hello World"));
qEdit.show();
// install signal handlers
QObject::connect(&qEdit, &LineEdit::editingFinished,
[&]() {
qDebug() << "Edit confirmed:" << qEdit.isConfirmed();
});
// runtime loop
return app.exec();
}
The output of Edit confirmed: true
was achieved by pressing Enter, the false
lines by clicking around.
This is done rather simple and might be done more sophisticated. However, it shows the principle achieved with a rather low number of code lines.