一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

用到的类:

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 }

 

posted on 2020-05-06 14:40  一杯清酒邀明月  阅读(6328)  评论(0编辑  收藏  举报