Short version
How do you implement undo functionality for edits made on QListWidgetItems
in PySide/PyQt?
Hint from a Qt
I would do it like this:
Create a custom QItemDelegate and use these two signals:
editorEvent
closeEditor
On editorEvent
: Save current state
On closeEditor
: Get new state and create a QUndoCommand
that set the new state for Redo
and the old state for Undo
.
That tutorial you mentioned is really not very helpful. There are indeed many approaches to undo-redo implementation for views, we just need to choose the simplest one. If you deal with small lists, the simpliest way is to save all data on each change and restore full list from scratch on each undo or redo operation.
If you still want atomic changes list, you can track user-made edits with QListWidget::itemChanged
signal. There are two problems with that:
QObject::blockSignals
calls to block unwanted signals.QListWidget::currentItemChanged
assuming that user won't find a way to edit an item without making is current first.So this is the changes that will make it work (besides adding ItemIsEditable
flag in two places):
def __init__(self):
#...
self.todoList.itemChanged.connect(self.itemChanged)
self.todoList.currentItemChanged.connect(self.currentItemChanged)
self.textBeforeEdit = ""
def itemChanged(self, item):
command = CommandEdit(self.todoList, item, self.todoList.row(item),
self.textBeforeEdit,
"Rename item '{0}' to '{1}'".format(self.textBeforeEdit, item.text()))
self.undoStack.push(command)
def currentItemChanged(self, item):
self.textBeforeEdit = item.text()
And the new change class:
class CommandEdit(QtGui.QUndoCommand):
def __init__(self, listWidget, item, row, textBeforeEdit, description):
super(CommandEdit, self).__init__(description)
self.listWidget = listWidget
self.textBeforeEdit = textBeforeEdit
self.textAfterEdit = item.text()
self.row = row
def redo(self):
self.listWidget.blockSignals(True)
self.listWidget.item(self.row).setText(self.textAfterEdit)
self.listWidget.blockSignals(False)
def undo(self):
self.listWidget.blockSignals(True)
self.listWidget.item(self.row).setText(self.textBeforeEdit)
self.listWidget.blockSignals(False)
Each time you verify and accept the new text of the item, save it as list item data. Quasi-semi-pseudo-code:
OnItemEdited(Item* item)
{
int dataRole{ 32 }; //or greater (see ItemDataRole documentation)
if (Validate(item->text()) {
item->setData(dataRole, item->text());
} else { //Restore previous value
item->setText(item->data(dataRole).toString());
}
}
I'm sorry if it looks too much like C++.