QT:使用redo、undo实现撤销快捷键

目标:在界面里添加撤销和回撤快捷键

实质:

撤销的实质就是利用了栈。每次都压入一个redo和undo。redo中记录当前的操作,undo中记录上次的操作。在执行撤回的时候,就从栈中将上次压入的redo和undo push出来,赋值给目标。

方法:

这里讲述在QGraphicsView里的应用,使用QUndoStack。

在RoboViewScene中添加键盘事件,判断是不是QKeySequence::UndoQKeySequence::Redo,调用对应的redo和undo函数

在ViewCommon头文件中包含#include ,并定义一个私有的QUndoStack。

在需要撤回的Item.cpp函数里将每次操作都写入undoMap和redoMap,通过push压入undoStack栈中。

这里定义了一个文件CommandPeoperty,用于处理传输undo、redo。在该头文件中需要重写undo()和redo()函数(举例:void undo() Q_DECL_OVERRIDE;),在cpp中在redo和undo中完成对应数据的赋值等操作。【注意】在Item中执行push后会立即出发redo(),所以在redo中需要加一次判断,让第一次放入不执行操作。

逻辑:

在Item里面存储上一次的数据和当前的数据,push压入undoStack栈中。

会立即触发redo()(这里需要处理第一次触发时不执行操作),但在CommandProperty初始化时,会将数据存入data的_undoData和_redoData,再将data存入_itemData。

当后面再次redo和undo时,会遍历_itemData,将最新的redodata和undodata赋值给item,实现撤销与回撤。

代码(逻辑是这么个逻辑):

在对应ViewScene的cpp中添加键盘响应

void ViewScene::keyPressEvent(QKeyEvent *event)
{
    if (event == QKeySequence::Undo)
    {
        View11Common::init()->undoStack()->undo();
    }
    if (event == QKeySequence::Redo)
    {
        View11Common::init()->undoStack()->redo();
    }
}

ViewCommon.h

#ifndef ViewCommon_H
#define ViewCommon_H

#include 
#include 
#include 
#include 
#include "Graphics11View.h"

class  ViewCommon : public QObject
{
    Q_OBJECT
 private:
     QUndoStack *_undoStack {Q_NULLPTR};
};
#endif // ViewCommon_H

在需要的存储redo和undo数据的地方插入下面的代码块,替换preValue、newValue、key

//添加undo、redo
QVariantMap undoMap,redoMap;
undoMap.insert(key,preValue);
_item->setUndoData(undoMap);
redoMap.insert(key,newValue);
_item->setRedoData(redoMap);
auto cmd = new Command11Property(QList() << _item);
View11Common::init()->undoStack()->push(cmd);
CommandProperty.h
#ifndef COMMANDPROPERTY_H
#define COMMANDPROPERTY_H

#include "CommandBase.h"
#include 

class CommandProperty:public CommandBase
{
public:
    CommandProperty(const QList &itemList,RoboViewScene* scene = Q_NULLPTR, QUndoCommand *parent=Q_NULLPTR);
    class ItemUndoRedoData{
    public:
        QGraphicsItem*_item {Q_NULLPTR}; //item指针
        QVariantMap _undoData;
        QVariantMap _redoData;
    };
    void keyPressEvent(QKeyEvent  *event);
    ~CommandProperty();

    void undo() Q_DECL_OVERRIDE;
    void redo() Q_DECL_OVERRIDE;
    QVariant _changeData;
    bool isBlockOneRedo() const;
    void setIsBlockOneRedo(bool isBlockOneRedo);
signals:
    void change(const QVariant &value);
private:
    QList _itemData;
    bool _isBlockOneRedo = false; //锁定一次redo
};
#endif // COMMANDPROPERTY_H

CommandProperty.cpp

#include "CommandProperty.h"
#include "items/BaseItem.h"

//
/// \param itemList
/// \param scene
/// \param parent
///
CommandProperty::CommandProperty(const QList &itemList,RoboViewScene* scene, QUndoCommand *parent)
    :CommandBase(scene,parent)
{
    foreach (auto &item, itemList) {
        ItemUndoRedoData * data = new ItemUndoRedoData;
        data->_item = item;
        auto bItem = dynamic_cast(item);
        data->_undoData = bItem->getUndoData().toMap();
        data->_redoData = bItem->getRedoData().toMap();
        _itemData.append(data);
    }
    setIsBlockOneRedo(true);
}

CommandProperty::~CommandProperty()
{
    qDeleteAll(_itemData);
}

void CommandProperty::undo()//撤销
{
    foreach (auto &item, _itemData) {
        auto bItem = dynamic_cast(item->_item);
        bItem->setUndoData(item->_undoData);
        //根据key值设置,需要将撤销后的值写入pw显示
        //SCInfo<<"   bItem:     "<getUndoData();
        bItem->sendUndodataToBottom();//将撤销信号传给目标

    }
    setIsBlockOneRedo(false);
}

//push后立即触发
void CommandProperty::redo()
{
    if(!isBlockOneRedo()){//第一次放入不触发
        foreach (auto &item, _itemData) {
            auto bItem = dynamic_cast(item->_item);
            bItem->setRedoData(item->_redoData);
            //SCInfo<<"   bItem:     "<getRedoData();
            bItem->sendRedodataToBottom();
        }
    }
    setIsBlockOneRedo(false);
}

bool CommandProperty::isBlockOneRedo() const
{
    return _isBlockOneRedo;
}

void CommandProperty::setIsBlockOneRedo(bool isBlockOneRedo)
{
    _isBlockOneRedo = isBlockOneRedo;
}


BaseItem.h

#ifndef BaseItem_H
#define BaseItem_H
class BaseObjectItem : public QObject
{
    Q_OBJECT
public:
    BaseObjectItem(QGraphicsItem *parentItem = Q_NULLPTR);
    ~BaseObjectItem() override;

    QVariant getUndoData() const;
    void setUndoData(const QVariant &undoData);

    QVariant getRedoData() const;
    void setRedoData(const QVariant &redoData);

protected:
    QVariant _undoData;
    QVariant _redoData;
};

#endif //BaseItem_H

BaseItem.cpp

#include "Base11Item.h"
BaseObjectItem::BaseObjectItem(QGraphicsItem *parentItem)
    : BaseItem (parentItem)
{
}

BaseObjectItem::~BaseObjectItem()
{
}

QVariant BaseObjectItem::getUndoData() const
{
    return _undoData;
}

void BaseObjectItem::setUndoData(const QVariant &undoData)
{
    _undoData = undoData;
}

QVariant BaseObjectItem::getRedoData() const
{
    return _redoData;
}

void BaseObjectItem::setRedoData(const QVariant &redoData)
{
    _redoData = redoData;
}

第二种写法:

CommandProperty.cpp

#include "CommandProperty.h"
#include "items/BaseItem.h"

//
/// \param itemList
/// \param scene
/// \param parent
///
CommandProperty::CommandProperty(const QList &itemList,RoboViewScene* scene, QUndoCommand *parent)
    :CommandBase(scene,parent)
{
    foreach (auto &item, itemList) {
        auto bItem = dynamic_cast(item);
        bItem->initRedoData();   //在调用CommandProperty函数前,先对item执行initUndoData
        ItemUndoRedoData * data = new ItemUndoRedoData;
        data->_item = item;
        data->_undoData = bItem->getUndoData();
        data->_redoData = bItem->getRedoData();
        _itemData.append(data);
    }
}

CommandProperty::~CommandProperty()
{
}

void CommandProperty::undo()//撤销
{
    update(false);   
}

//push后立即触发
void CommandProperty::redo()
{
    update(true);
}

void CommandProperty::update(bool isVisible)
{
    if(isVisible){
        foreach (auto &item, _itemData) {
            auto bItem = dynamic_cast(item->_item);
            bItem->redoStacKData(item->_redoData);
        }
    }else{
        foreach (auto &item, _itemData) {
            auto bItem = dynamic_cast(item->_item);
            bItem->undoStacKData(item->_undoData);
        }
    }
}

CommandProperty.h

#ifndef COMMANDPROPERTY_H
#define COMMANDPROPERTY_H

#include "CommandBase.h"
#include 

class CommandProperty:public CommandBase
{
public:
    CommandProperty(const QList &itemList,RoboViewScene* scene = Q_NULLPTR, QUndoCommand *parent=Q_NULLPTR);
    
    class ItemUndoRedoData{
    public:
        QGraphicsItem*_item {Q_NULLPTR}; //item指针
        QVariant _undoData;
        QVariant _redoData;
    };

    ~CommandProperty();

    void updateData(bool isvisible);
protected:
    void undo() ;
    void redo() ;
private:
    QList _itemData;
};
#endif // COMMANDPROPERTY_H
BaseObjectItem.cpp
/**记录item需要undo,redo,数据
 * @brief BaseObjectItem::initUndoData
 */
void BaseObjectItem::initUndoData()
{
    QVariantMap map;
    map.insert("pos",_pos);
    BaseObjectItem *group = dynamic_cast< BaseObjectItem *>(this->parentItem());
    map.insert("parent",QVariant::fromValue((void *) group));
    _undoData = map;
}

/**记录item需要undo,redo,数据
 * @brief BaseObjectItem::initUndoData4
 */
void BaseObjectItem::initRedoData()
{
    QVariantMap map;
    map.insert("pos",this->pos());
    BaseObjectItem *group = dynamic_cast< BaseObjectItem *>(this->parentItem());
    map.insert("parent",QVariant::fromValue((void *) group));
    _redoData = map;
}

/**根据undo,数据重新初始化item
 * @brief BaseObjectItem::undoStacKData
 */
void BaseObjectItem::undoStacKData(const QVariant &var)
{
    if(!var.isNull() && !var.toMap().isEmpty()){
        QVariant varp=var.toMap().value("parent");
        BaseObjectItem* parent = (BaseObjectItem*)varp.value();
        QPointF pos = var.toMap().value("pos").toPointF();
        this->setParentItem(parent);
        this->setPos(pos);
        SCDebug<<"undo";
    }
}

/**根据redo,数据重新初始化item
 * @brief BaseObjectItem::undoStacKData
 */
void BaseObjectItem::redoStacKData(const QVariant &var)
{
    if(!var.isNull() && !var.toMap().isEmpty()){
        QVariant varp=var.toMap().value("parent");
        BaseObjectItem* parent = (BaseObjectItem*)varp.value();
        QPointF pos = var.toMap().value("pos").toPointF();
        this->setParentItem(parent);
        this->setPos(pos);
        SCDebug<<"redo";
    }
}

你可能感兴趣的:(qt)