В моем проекте я подкласс QStyledItemDelegate
и вернул пользовательский редактор из createEditor
функция.
QWidget* TagEditDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
TagEditWidget* tagEditWidget = new TagEditWidget(parent, index.data(Qt::UserRole+4).toInt(), index.data(Qt::UserRole+2).toByteArray(), index.data(Qt::UserRole+3).toByteArray(), index.parent().data(Qt::UserRole+4).toInt() == 9, parent->width());
return tagEditWidget; //tagEditWidget is my custom QWidget
}
Когда редактирование закончится, я хочу записать новые данные обратно в модель. Итак, я переиграл setModelData
,
void TagEditDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
TagEditWidget * tagEditWidget = qobject_cast<TagEditWidget*>(editor);
if (!tagEditWidget)
{
QStyledItemDelegate::setModelData(editor, model, index);
return;
}
//Edit model here?
}
Это работает, но проблема в том, что setModelData
вызывается независимо от того, КАК редактор был закрыт. Я только хочу записать новые данные, если редактор закрыт с помощью EndEditHint
, QAbstractItemDelegate::SubmitModelCache
, Поэтому я подключил closeEditor
сигнал к слоту я сделал называется editFinished
,
connect(this, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(editFinished(QWidget*,QAbstractItemDelegate::EndEditHint)));
Так что теперь я вижу, как редактор закрывается через EndEditHint
и если я должен записать данные обратно в модель. Buuuuut, setModelData
получить называется до closeEditor
сигнал. Как записать данные обратно в модель, когда closeEditor
сигнал вызывается последним? Я что-то здесь упускаю?
Основной ответ:
Ваша концепция кажется хорошей почти до конца. Я бы сосредоточился на TagEditDelegate::setModelData
метод.
Если вы на самом деле не хотите обновлять данные в модели, просто убедитесь, что они не изменились. Это означает, что когда oldData == newData
просто return;
и пропустить обновления модели.
Дополнительные примечания:
Глядя на создание вашего редактора, у меня складывается впечатление, что в нем нет ни одного значения, представленного пользователю. Чтобы облегчить передачу аргумента и сравнить данные редактора, подумайте о создании отдельного class/struct
для этого. Чтобы вы могли позвонить:
new TagEditWidget(parent, editorData, parent->width())
где EditorData будет вашим классом / структурой, которая может быть получена отдельной функцией:
EditorData editorData = readEditorData(index);
Функция может быть повторно использована в setModelData
Способ проверки состояния:
if (tagEditWidget->getEditorData() == readEditorData(index)) return;
Также избегайте использования магических чисел, таких как Qt::UserRole+2
, Создайте свой собственный enum для указания необходимых ролей. Например:
enum class MyRole
{
Data1 = Qt::UserRole,
Data2,
Data3,
};
РЕДАКТИРОВАТЬ согласно обсуждению в комментариях
Если вы хотите узнать, действительно ли пользователь не отменил издание тем или иным способом, вы можете переопределить eventFilter
либо внутри редактора, либо делегата.
При создании звонка редактора installEventFilter
в конструкторе. Ваш eventFilter
Реализация может выглядеть так:
bool eventFilter(QObject *object, QEvent *event) override
{
if (event->type() == QEvent::KeyPress)
{
const auto key = static_cast<QKeyEvent *>(event)->key();
if (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Tab)
submitted = true;
}
else if (event->type() == QEvent::FocusAboutToChange &&
static_cast<QFocusEvent*>(event)->reason() == Qt::MouseFocusReason)
{
submitted = true;
}
// extetend the conditions (add else if) to include
// events which you might want to treat as submitted
return QLineEdit::eventFilter(object, event);
}
куда submitted
это bool
член редактора инициализирован в false
в конструкторе. Тогда вы можете создать метод получения isSubmitted()
и вы готовы проверить статус внутри setModelData
метод.
if (tagEditWidget->isSubmitted())
{
// process data or update model here
}
Я просто ответил на свой вопрос по-своему. Думаю, решение Дустеха «правильнее».
Я сделал bool в классе делегата под названием shouldCommit
и установите его в ложь.
Затем в setModelData
Я проверяю, должен ли я записать данные в модель.
void TagEditDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
TagEditWidget * tagEditWidget = qobject_cast<TagEditWidget*>(editor);
if (!tagEditWidget)
{
QStyledItemDelegate::setModelData(editor, model, index);
return;
}
if(shouldCommit)
{
//write data here
}
}
Затем, когда closeEditor
сигнал испускается, проверяю подсказку и временно устанавливаю shouldCommit
чтобы правда и зов commitData
снова. Теперь, когда shouldCommit
верно, данные записываются.
void TagEditDelegate::editFinished(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
{
if(hint == QAbstractItemDelegate::SubmitModelCache)
{
TagEditWidget * tagEditWidget = qobject_cast<TagEditWidget*>(editor);
if(tagEditWidget)
{
shouldCommit = true;
commitData(editor);
shouldCommit = false;
}
}
}
Хотя это хорошо работает для моего варианта использования, это может быть не для всех, и раздел комментариев Dusteh может быть более полезным для некоторых. Его метод состоит в том, чтобы переопределить eventFilter
и написать свою собственную реализацию о том, когда позвонить commitData
, Это, вероятно, более подходящее решение для людей, которые хотят больше контроля.
Но для тех, кто хочет использовать по умолчанию eventFilter
реализация и просто посмотреть на EndEditHint
вот мое решение.