用到的类:
1 QUndoStack: 一个存放 QUndoCommand 命令的栈.
2 QUndoCommand:The QUndoCommand class is the base class of all commands stored on a QUndoStack.
3 QUndoView:The QUndoView class displays the contents of a QUndoStack.(显示QUndoStack的内容)
下面的例子是根据 Qt 自带的例子(undoframework)写的:
重写 QGraphicsPolygonItem (方块)
myitem.h
1 #ifndef MYITEM_H
2 #define MYITEM_H
3
4 #include <QGraphicsItem>
5
6 class myItem :public QGraphicsPolygonItem
7 {
8
9 public:
10
11 enum {Type = UserType +1};
12
13 explicit myItem(QGraphicsItem *parent = 0);
14
15 int type() const override{return Type;}
16 private:
17 QPolygonF m_boxItem;
18 };
19
20 #endif // MYITEM_H
myitem.cpp
1 #include "myitem.h"
2 #include <QBrush>
3 myItem::myItem(QGraphicsItem *parent)
4 {
5
6 m_boxItem << QPointF(0,0) << QPointF(30,0)
7 << QPointF(30,30) << QPointF(0,30)
8 << QPointF(0,0);
9 setPolygon(m_boxItem);
10 //颜色随机
11 QColor color( (qrand() % 256),(qrand() % 256),(qrand() % 256) );
12
13 QBrush brush(color);
14 setBrush(brush);
15 //可移动
16 setFlag(QGraphicsItem::ItemIsMovable);
17 //可选中
18 setFlag(QGraphicsItem::ItemIsSelectable);
19 }
重写 QGraphicsScene(场景)
myscene.h
1 #ifndef MYSCENE_H
2 #define MYSCENE_H
3
4 #include <QGraphicsScene>
5 #include <QObject>
6 #include "myitem.h"
7 class myScene : public QGraphicsScene
8 {
9 Q_OBJECT
10 public:
11 myScene(QObject *parent = 0);
12 signals:
13
14 void itemMoveSignal(myItem* item,const QPointF position);
15
16 protected:
17 void mousePressEvent(QGraphicsSceneMouseEvent *event);
18 void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
19
20 private:
21
22 QGraphicsItem * m_Item;
23 QPointF m_oldPos;
24 };
25
26 #endif // MYSCENE_H
myscene.cpp
1 #include "myscene.h"
2 #include <QGraphicsSceneMouseEvent>
3 #include <QDebug>
4 myScene::myScene(QObject *parent)
5 {
6 m_Item = 0;
7
8 }
9
10 void myScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
11 {
12 QPointF mousePos (event->buttonDownScenePos(Qt::LeftButton).x(),
13 event->buttonDownScenePos(Qt::LeftButton).y());
14 const QList<QGraphicsItem* >itemList = items(mousePos);
15
16 m_Item = itemList.isEmpty() ? 0 :itemList.first();
17
18 if(m_Item != 0 && event->button() == Qt::LeftButton)
19 m_oldPos = m_Item->pos();
20
21 QGraphicsScene::mousePressEvent(event);
22
23 }
24
25 void myScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
26 {
27 if(m_Item != 0 && event->button() == Qt::LeftButton)
28 {
29 if(m_oldPos != m_Item->pos())
30 //发送位置移动的信号
31 emit itemMoveSignal(qgraphicsitem_cast<myItem*>(m_Item),m_oldPos);
32 m_Item = 0;
33 }
34 QGraphicsScene::mouseReleaseEvent(event);
35 }
下面重写的 QUndoCommand 才是实现撤回和回撤的模块
重写 QUndoCommand 就是重写父类的 undo() 和 redo() 方法
mycommand.h
1 #ifndef MYCOMMAND_H
2 #define MYCOMMAND_H
3
4 #include <QUndoCommand>
5 #include "myitem.h"
6 #include "myscene.h"
7 //添加item
8 class addCommand :public QUndoCommand
9 {
10 public :
11 addCommand(QGraphicsScene* graphicsScene,QUndoCommand* parent = 0);
12
13 void redo() override;//重写这两个函数
14 void undo() override;
15 private:
16
17 myItem* m_item;
18
19 QGraphicsScene* m_scene;
20
21 QPointF m_initPos;
22 };
23 //移动item
24 class moveCommand:public QUndoCommand
25 {
26 public:
27 moveCommand(myItem* item,const QPointF oldPos,QUndoCommand* parent = 0);
28
29 void redo() override;//重写这两个函数
30 void undo() override;
31 private:
32 myItem* m_item;
33 QPointF m_oldPos;
34 QPointF m_newPos;
35
36 };
37
38 #endif // MYCOMMAND_H
mycommand.cpp
1 #include "mycommand.h"
2
3
4 addCommand::addCommand(QGraphicsScene *graphicsScene, QUndoCommand *parent)
5 {
6 m_scene = graphicsScene;
7
8 m_item = new myItem();
9
10 m_initPos = QPointF(10,10); //初始化item 生成的位置
11
12 setText("add item");//undoView 中就会显示(父类的方法)
13 }
14
15 void addCommand::redo()//stack push 时 会自动调用
16 {
17 m_scene->addItem(m_item);
18 m_item->setPos(m_initPos);
19 m_scene->clearSelection();
20 m_scene->update();
21 }
22
23 void addCommand::undo()
24 {
25 m_scene->removeItem(m_item);
26 m_scene->update();
27 }
28
29 moveCommand::moveCommand(myItem *item, const QPointF oldPos, QUndoCommand *parent)
30 {
31 m_item = item;
32
33 m_newPos = m_item->pos();
34
35 m_oldPos = oldPos;
36 }
37
38 void moveCommand::redo()
39 {
40 m_item->setPos(m_newPos);
41 setText(QString("Move Item:(%1,%2)").arg(m_item->pos().rx()).arg(m_item->pos().ry()));
42 }
43
44 void moveCommand::undo()
45 {
46 m_item->setPos(m_oldPos);
47 m_item->scene()->update();
48 setText(QString("Move Item:(%1,%2)").arg(m_item->pos().rx()).arg(m_item->pos().ry()));
49 }
主界面
widget.h
1 #ifndef WIDGET_H
2 #define WIDGET_H
3
4 #include <QWidget>
5 #include <QPushButton>
6 #include <QGraphicsView>
7 #include <QUndoStack>
8 #include <QUndoView>
9
10 #include "myscene.h"
11 #include "myitem.h"
12 #include "mycommand.h"
13 namespace Ui {
14 class Widget;
15 }
16
17 class Widget : public QWidget
18 {
19 Q_OBJECT
20
21 public:
22 explicit Widget(QWidget *parent = 0);
23 ~Widget();
24
25 void initUi();
26
27 void initAction();
28
29 void addItem();
30
31 void itemMoved(myItem* item,QPointF pos);
32
33 private:
34 Ui::Widget *ui;
35 QPushButton* m_addItemBtn;
36 QAction* m_undoAction;
37 QAction* m_redoAction;
38 myScene *m_scene;
39
40 QUndoStack* m_undoStack;
41 QUndoView* m_undoView;
42 };
43
44 #endif // WIDGET_H
widget.cpp
1 #include "widget.h"
2 #include "ui_widget.h"
3 #include <QLayout>
4
5 Widget::Widget(QWidget *parent) :
6 QWidget(parent),
7 ui(new Ui::Widget)
8 {
9 ui->setupUi(this);
10
11
12 initAction();
13
14 initUi();
15 }
16
17 Widget::~Widget()
18 {
19 delete ui;
20 }
21
22 void Widget::initUi()
23 {
24 this->setWindowTitle("码农小明--撤销回撤");
25
26 m_addItemBtn = new QPushButton();
27 m_addItemBtn->setText("add Item");
28
29 connect(m_addItemBtn,&QPushButton::clicked,this,&Widget::addItem);
30
31 m_scene = new myScene();
32 QBrush brush(Qt::gray);
33 m_scene->setSceneRect(QRect(0,0,200,300));
34 m_scene->setBackgroundBrush(brush);
35
36 connect(m_scene,&myScene::itemMoveSignal,this,&Widget::itemMoved);
37
38
39 QGraphicsView *view = new QGraphicsView(m_scene);
40
41 QVBoxLayout *pLayout = new QVBoxLayout();
42 pLayout->addWidget(m_addItemBtn);
43 pLayout->addWidget(view);
44
45
46 m_undoView = new QUndoView(m_undoStack);//右面显示栈内容的view(不setText就是空的)
47 QHBoxLayout *pHLayout = new QHBoxLayout();
48 pHLayout->addLayout(pLayout);
49 pHLayout->addWidget(m_undoView);
50
51
52 this->setLayout(pHLayout);
53
54 this->resize(500,400);
55
56 }
57
58 void Widget::initAction()
59 {
60 m_undoStack = new QUndoStack(this);//存放操作的栈
61
62 m_undoAction = m_undoStack->createUndoAction(this,"Undo");
63 m_undoAction->setShortcut(QKeySequence::Undo);
64
65 m_redoAction = m_undoStack->createRedoAction(this,"Redo");
66 m_redoAction->setShortcut(QKeySequence::Redo);
67
68 this->addAction(m_undoAction);
69 this->addAction(m_redoAction);
70 }
71
72 void Widget::addItem()
73 {
74 QUndoCommand* add = new addCommand(m_scene);
75 m_undoStack->push(add);//入栈操作 会自动调用 addCommand 的 redo
76
77 }
78
79 void Widget::itemMoved(myItem *item, QPointF pos)
80 {
81 m_undoStack->push(new moveCommand(item,pos));//入栈操作
82 }