-
用到的类:
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 }