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)写的:
myitem.h
#ifndef MYITEM_H
#define MYITEM_H
#include
class myItem :public QGraphicsPolygonItem
{
public:
enum {Type = UserType +1};
explicit myItem(QGraphicsItem *parent = 0);
int type() const override{return Type;}
private:
QPolygonF m_boxItem;
};
#endif // MYITEM_H
myitem.cpp
#include "myitem.h"
#include
myItem::myItem(QGraphicsItem *parent)
{
m_boxItem << QPointF(0,0) << QPointF(30,0)
<< QPointF(30,30) << QPointF(0,30)
<< QPointF(0,0);
setPolygon(m_boxItem);
//颜色随机
QColor color( (qrand() % 256),(qrand() % 256),(qrand() % 256) );
QBrush brush(color);
setBrush(brush);
//可移动
setFlag(QGraphicsItem::ItemIsMovable);
//可选中
setFlag(QGraphicsItem::ItemIsSelectable);
}
myscene.h
#ifndef MYSCENE_H
#define MYSCENE_H
#include
#include
#include "myitem.h"
class myScene : public QGraphicsScene
{
Q_OBJECT
public:
myScene(QObject *parent = 0);
signals:
void itemMoveSignal(myItem* item,const QPointF position);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
private:
QGraphicsItem * m_Item;
QPointF m_oldPos;
};
#endif // MYSCENE_H
myscene.cpp
#include "myscene.h"
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
myScene::myScene(QObject *parent)
{
m_Item = 0;
}
void myScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QPointF mousePos (event->buttonDownScenePos(Qt::LeftButton).x(),
event->buttonDownScenePos(Qt::LeftButton).y());
const QList<QGraphicsItem* >itemList = items(mousePos);
m_Item = itemList.isEmpty() ? 0 :itemList.first();
if(m_Item != 0 && event->button() == Qt::LeftButton)
m_oldPos = m_Item->pos();
QGraphicsScene::mousePressEvent(event);
}
void myScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(m_Item != 0 && event->button() == Qt::LeftButton)
{
if(m_oldPos != m_Item->pos())
//发送位置移动的信号
emit itemMoveSignal(qgraphicsitem_cast<myItem*>(m_Item),m_oldPos);
m_Item = 0;
}
QGraphicsScene::mouseReleaseEvent(event);
}
重写 QUndoCommand 就是重写父类的 undo() 和 redo() 方法
mycommand.h
#ifndef MYCOMMAND_H
#define MYCOMMAND_H
#include
#include "myitem.h"
#include "myscene.h"
//添加item
class addCommand :public QUndoCommand
{
public :
addCommand(QGraphicsScene* graphicsScene,QUndoCommand* parent = 0);
void redo() override;//重写这两个函数
void undo() override;
private:
myItem* m_item;
QGraphicsScene* m_scene;
QPointF m_initPos;
};
//移动item
class moveCommand:public QUndoCommand
{
public:
moveCommand(myItem* item,const QPointF oldPos,QUndoCommand* parent = 0);
void redo() override;//重写这两个函数
void undo() override;
private:
myItem* m_item;
QPointF m_oldPos;
QPointF m_newPos;
};
#endif // MYCOMMAND_H
mycommand.cpp
#include "mycommand.h"
addCommand::addCommand(QGraphicsScene *graphicsScene, QUndoCommand *parent)
{
m_scene = graphicsScene;
m_item = new myItem();
m_initPos = QPointF(10,10); //初始化item 生成的位置
setText("add item");//undoView 中就会显示(父类的方法)
}
void addCommand::redo()//stack push 时 会自动调用
{
m_scene->addItem(m_item);
m_item->setPos(m_initPos);
m_scene->clearSelection();
m_scene->update();
}
void addCommand::undo()
{
m_scene->removeItem(m_item);
m_scene->update();
}
moveCommand::moveCommand(myItem *item, const QPointF oldPos, QUndoCommand *parent)
{
m_item = item;
m_newPos = m_item->pos();
m_oldPos = oldPos;
}
void moveCommand::redo()
{
m_item->setPos(m_newPos);
setText(QString("Move Item:(%1,%2)").arg(m_item->pos().rx()).arg(m_item->pos().ry()));
}
void moveCommand::undo()
{
m_item->setPos(m_oldPos);
m_item->scene()->update();
setText(QString("Move Item:(%1,%2)").arg(m_item->pos().rx()).arg(m_item->pos().ry()));
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include "myscene.h"
#include "myitem.h"
#include "mycommand.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void initUi();
void initAction();
void addItem();
void itemMoved(myItem* item,QPointF pos);
private:
Ui::Widget *ui;
QPushButton* m_addItemBtn;
QAction* m_undoAction;
QAction* m_redoAction;
myScene *m_scene;
QUndoStack* m_undoStack;
QUndoView* m_undoView;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
initAction();
initUi();
}
Widget::~Widget()
{
delete ui;
}
void Widget::initUi()
{
this->setWindowTitle("码农小明--撤销回撤");
m_addItemBtn = new QPushButton();
m_addItemBtn->setText("add Item");
connect(m_addItemBtn,&QPushButton::clicked,this,&Widget::addItem);
m_scene = new myScene();
QBrush brush(Qt::gray);
m_scene->setSceneRect(QRect(0,0,200,300));
m_scene->setBackgroundBrush(brush);
connect(m_scene,&myScene::itemMoveSignal,this,&Widget::itemMoved);
QGraphicsView *view = new QGraphicsView(m_scene);
QVBoxLayout *pLayout = new QVBoxLayout();
pLayout->addWidget(m_addItemBtn);
pLayout->addWidget(view);
m_undoView = new QUndoView(m_undoStack);//右面显示栈内容的view(不setText就是空的)
QHBoxLayout *pHLayout = new QHBoxLayout();
pHLayout->addLayout(pLayout);
pHLayout->addWidget(m_undoView);
this->setLayout(pHLayout);
this->resize(500,400);
}
void Widget::initAction()
{
m_undoStack = new QUndoStack(this);//存放操作的栈
m_undoAction = m_undoStack->createUndoAction(this,"Undo");
m_undoAction->setShortcut(QKeySequence::Undo);
m_redoAction = m_undoStack->createRedoAction(this,"Redo");
m_redoAction->setShortcut(QKeySequence::Redo);
this->addAction(m_undoAction);
this->addAction(m_redoAction);
}
void Widget::addItem()
{
QUndoCommand* add = new addCommand(m_scene);
m_undoStack->push(add);//入栈操作 会自动调用 addCommand 的 redo
}
void Widget::itemMoved(myItem *item, QPointF pos)
{
m_undoStack->push(new moveCommand(item,pos));//入栈操作
}
98年菜鸡一枚,请大佬们多多关照!