一、功能
通过给 QGraphicsScene 添加、删除、移动 QGraphicsPolygonItem 来演示 撤销重做功能
标签 undo framework example
二、核心代码,以添加图例为例
MainWindow.cpp 的核心代码
//1 创建堆栈
undoStack = new QUndoStack(this); //2 以列表的形式显示堆栈信息(命令名称)
undoView = new QUndoView(undoStack);
undoView->setWindowTitle(tr("Command List"));
undoView->show();
undoView->setAttribute(Qt::WA_QuitOnClose, false);//3 添加菜单动作:添加正方形图例addBoxAction = new QAction(tr("Add &Box"), this);addBoxAction->setShortcut(tr("Ctrl+O"));connect(addBoxAction, SIGNAL(triggered()), this, SLOT(addBox()));//4 添加菜单动作:撤销、重做 undoAction = undoStack->createUndoAction(this, tr("&Undo"));undoAction->setShortcuts(QKeySequence::Undo);redoAction = undoStack->createRedoAction(this, tr("&Redo"));redoAction->setShortcuts(QKeySequence::Redo);//5 添加正方形图例的动作
void MainWindow::addBox()
{//创建添加命令QUndoCommand *addCommand = new AddCommand(DiagramItem::Box, diagramScene);//放入堆栈,,放入后 undoStack 会主动调用 addCommand 的 redo 动作undoStack->push(addCommand);
}
添加命令的代码
class AddCommand : public QUndoCommand
{
public:AddCommand(DiagramItem::DiagramType addType, QGraphicsScene *graphicsScene,QUndoCommand *parent = 0);~AddCommand();void undo() override;void redo() override;private:DiagramItem *myDiagramItem; QGraphicsScene *myGraphicsScene; QPointF initialPosition;
};AddCommand::AddCommand(DiagramItem::DiagramType addType,QGraphicsScene *scene, QUndoCommand *parent): QUndoCommand(parent)
{static int itemCount = 0;myGraphicsScene = scene; //保存场景,也就是接受者myDiagramItem = new DiagramItem(addType);initialPosition = QPointF((itemCount * 15) % int(scene->width()),(itemCount * 15) % int(scene->height()));scene->update();++itemCount;setText(QObject::tr("Add %1").arg(createCommandString(myDiagramItem, initialPosition)));
}AddCommand::~AddCommand()
{if (!myDiagramItem->scene())delete myDiagramItem;
}//撤销动作:从场景中删除当前item
void AddCommand::undo()
{myGraphicsScene->removeItem(myDiagramItem);myGraphicsScene->update();
}//重做动作:将当前item 重新添加到场景
void AddCommand::redo()
{myGraphicsScene->addItem(myDiagramItem);myDiagramItem->setPos(initialPosition); //初始位置myGraphicsScene->clearSelection();myGraphicsScene->update();
}
三、撤销重做是 命令模式的一种体现
- 命令(Command):定义命令的接口,通常包含执行和撤销两个方法。
- 具体命令(ConcreteCommand):实现命令接口,包含了对应的操作。
- 命令接收者(Receiver):执行命令的对象。
- 命令发起者(Invoker):调用命令的对象,负责将命令发送给命令接收者。
- 客户端(Client):创建命令对象并将其发送给命令发起者。
对于该范例
- 命令(Command):
QUndoCommand
是命令接口。它定义了执行(redo)和撤销(undo)的方法。 - 具体命令(ConcreteCommand):
AddCommand
是具体的命令。它是QUndoCommand
的子类,实现了redo
和undo
方法。 - 命令接收者(Receiver):
QGraphicsScene
是命令的接收者。它是执行命令的对象,AddCommand
的redo
和undo
方法都是操作QGraphicsScene
。 - 命令发起者(Invoker):
QUndoStack
是命令的发起者。它负责调用和存储命令。菜单栏的动作addBoxAction实际上也扮演了命令发起者的角色,因为它们是触发执行或撤销命令的实际用户界面元素。 - 客户端(Client):在这个例子中,MainWindow就是客户端。它创建了应用程序,包括命令接收者(
QGraphicsScene
)、命令发起者(QUndoStack
和 菜单栏)、并在 菜单动作执行时创建AddCommand 命令
。