@JonB said in Continue editing if ItemModel::setData fails:
@numzero
void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
That's a virtual protected slot. Wonder what happens if you override that, check validity, and not call base? Does that keep it open, and are you still in an acceptable state??
<...>
Just found https://stackoverflow.com/questions/54623332/qtableview-prevent-departure-from-cell-and-closure-of-delegate-editor
That does work, mostly. Here is a sample:
class MyView(QTreeView):
_allow_close_editor = True
def closeEditor(self, editor: QWidget, hint: QAbstractItemDelegate.EndEditHint):
if not self._allow_close_editor:
return
super().closeEditor(editor, hint)
self._allow_close_editor = True
def commitData(self, editor: QWidget):
self._allow_close_editor = False
super().commitData(editor)
def dataChanged(self, topLeft: QModelIndex, bottomRight: QModelIndex, roles: [int] = []):
super().dataChanged(topLeft, bottomRight, roles)
self._allow_close_editor = True
However, it behaves poorly if the user tries to select another item while his input for the current one is not valid: the focus changes, but the editor remains open. editor.setFocus() doesn’t help.
So here is a more advanced version, based on the SO answer:
class MyItemDelegate(QStyledItemDelegate):
def setModelData(self, editor: QWidget, model: QAbstractItemModel, index: QModelIndex) -> None:
n = editor.metaObject().userProperty().name()
if not n:
n = d.editorFactory().valuePropertyName(model.data(index, Qt.EditRole).userType())
if n:
commit_result = model.setData(index, editor.property(n), Qt.EditRole)
if not commit_result:
QMessageBox.warning(editor, 'Tree editor', 'Failed to update the value', QMessageBox.Ok)
editor.setFocus()
parent = editor
while parent:
if isinstance(parent, QAbstractItemView):
parent.setCurrentIndex(index)
break
parent = parent.parent()
editor.setProperty(MyView.COMMIT_RESULT_PROPERTY_NAME, commit_result)
class MyView(QTreeView):
COMMIT_RESULT_PROPERTY_NAME = '_myview_commit_result'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setItemDelegate(MyItemDelegate(parent=self))
def closeEditor(self, editor: QWidget, hint: QAbstractItemDelegate.EndEditHint):
commit_result = editor.property(self.COMMIT_RESULT_PROPERTY_NAME)
editor.setProperty(self.COMMIT_RESULT_PROPERTY_NAME, None)
if commit_result == False:
return
super().closeEditor(editor, hint)
This almost works. Showing UI works, keeping editor state works. Still, has some weird behavior, like visual and actual focus not matching.
Anyway, I suppose doing what I’m trying to do may not be exactly good idea anyway; it may be more confusing than helpful. And e.g. Dolphin does it differently: it lets the user continue editing in the dialog it shows, rather than in the view itself after showing the dialog. (Technically, it’s KIO what shows the dialog IIUC. And technically, Dolphin doesn’t use QTreeView either despite looking like one).