Пользовательский QStyledItemDelegate — применение изменений к модели

В моем проекте я подкласс 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 сигнал вызывается последним? Я что-то здесь упускаю?

0

Решение

Основной ответ:

Ваша концепция кажется хорошей почти до конца. Я бы сосредоточился на 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
}
1

Другие решения

Я просто ответил на свой вопрос по-своему. Думаю, решение Дустеха «правильнее».

Я сделал 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вот мое решение.

0

По вопросам рекламы [email protected]