Qt QUndoCommand(实现撤回和回撤)

用到的类:

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)写的:

Qt QUndoCommand(实现撤回和回撤)_第1张图片

重写 QGraphicsPolygonItem (方块)

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);
}

重写 QGraphicsScene(场景)

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 才是实现撤回和回撤的模块

重写 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年菜鸡一枚,请大佬们多多关照!

你可能感兴趣的:(Qt)