Я реализую редактируемое дерево со стеком отмены, используя QUndoStack. Удаление узла требует удаления всех его потомков. Я использую дочерний QUndoCommand для этого:
Заголовок:
class RemoveNodeCommand : QUndoCommand
{
public:
RemoveNodeCommand(NodesContainer *cont, int node_id, QUndoCommand *parent = 0);
void undo();
void redo();
private:
NodeParams mParams;
NodesContainer *mCont;
};
Реализация команды:
RemoveNodeCommand::RemoveNodeCommand(NodesContainer *cont, int node_id, QUndoCommand *parent)
{
QList<int> offsprings_ids;
QUndoCommand *tmpcomm;
//Keep params of deleted notes to use in Undo if necessary
mParams = cont->getNogeParams();
mCont = cont;
//List all offsprings of node to be deleted
cont->getOffspringsIds(&offsprings_ids);
for(int co = 0; co < basket.size(); co++)
{
tmpcomm = new RemoveNodeCommand(cont, offsprings_ids.at(co), this);
}
}
void RemoveNodeCommand::redo()
{
mCont->deleteNode(mParams);
}
Слот в классе редактирования дерева:
RemoveNodeSlot(int id)
{
mUndoStack->push( new RemoveNodeCommand(mContainer, id));
}
В документации QUndoStack указано, что
Если parent не равен 0, эта команда добавляется в дочерний список родителя.,
и это правда, child_list родительской команды увеличивается каждый раз, когда создается новая дочерняя команда.
Тем не менее, подталкивание родителя не приводит к выталкиванию всех его детей. redo () вызывается только для родительского узла.
Что я делаю не так? Нужно ли толкать всех детей вручную? И, поскольку мне нужны все эти команды внутри одного шага отмены, мне нужно будет использовать beginMacro и endMacro? Но так держать указатели на дочерние команды в QUndoCommand бессмысленно. Я думаю, что это не ошибка QT, а мое неправильное понимание концепции отмены стека. Как правильно использовать механизм дочерних команд?
Необходимо вызывать реализацию по умолчанию QUndoCommand :: undo () и QUndoCommand :: redo () во вложенных реализациях.
В redo () реализация по умолчанию должна быть добавлена перед обработкой данных, а в undo () — после того, как все данные дерева продолжаются.
void RemoveNodeCommand::redo()
{
QUndoCommand::redo();
mCont->deleteNode(mParams);
}
Дочерний командный конструктор должен передать родитель аргумент для конструктора QUndoCommand:
RemoveNodeCommand::RemoveNodeCommand(NodesContainer *cont, int node_id, QUndoCommand *parent)
: QUndoCommand(parent) {
// ...
}
Поскольку вы используете один и тот же класс для родителя и ребенка, вам также необходимо вызвать методы родительского вызова «Отменить + Повторить», как объяснил Дмитрий Курганский.