撤销的实质就是利用了栈。每次都压入一个redo和undo。redo中记录当前的操作,undo中记录上次的操作。在执行撤回的时候,就从栈中将上次压入的redo和undo push出来,赋值给目标。
这里讲述在QGraphicsView里的应用,使用QUndoStack。
在RoboViewScene中添加键盘事件,判断是不是QKeySequence::Undo和QKeySequence::Redo,调用对应的redo和undo函数。
在ViewCommon头文件中包含#include
在需要撤回的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";
}
}