撤销和重做(Undo和Redo)的C++完美实现(7-A)

本文介绍了如何使用C++的模板元编程技术来自动化实现复合对象类,包括创建、修改和删除操作,以实现撤销和重做功能。通过一个名为`compound`的模板类,作者展示了如何自动生成特定的复合类,如`RoundRectangle`,并提供了测试代码来演示这些操作。
摘要由CSDN通过智能技术生成
#if 0

在上一章里面讨论了复合对象的问题。RoundRectangle类(b)方案是一个复合类方案,
从上一章的内容我们可以看出虽然实现了一个RoundRectangle类确实是一个复合类,但是
实现复合类是一个比较繁琐的事情,对于一个程序里面有很多这种类的情况,那么前一章
里面的编码过程将会是一个非常繁琐的体力劳动。在这一章里面将使用模板元编程将这种
繁琐的体力劳动自动化。

在给出实现之前我们还是先把问题弄清楚!所以必须先回答下面的几个问题:

(1)自动化的目标是什么?关于这个问题,从前一章可以看出:很显然将要给出一个通
用的复合对象类。那么这个最终的类名,我命名为:“compound”,因为这个词具有化合
物的意思。学过高中化学的都知道,化合物可以是一种单质。很显然这里的复合对象类自
身是一个完备的整体,这一点和单质的概念有点类似:)。

(2)这个复合对象类的输入参数是什么?从前一章可以得出:传入的参数当然是各个属
性的类型信息串。这种类型串正好可以使用模板元编程基础与应用的cons类型串来实现。

(3)这个复合对象类具备有那些操作?从前一章可以看出:复合对象类很显然应该具备
有三种操作,分别是创建(create)、修改(modify)和删除(remove)。其中创建操作将会在
复合对象创建的同时把所有的属性类型的对象也创建一份;修改操作则可以直接修改每一
个属性对象,这是通过属性类型的静态转型来实现的;删除操作将会在复合对象删除之前
把所有的属于该复合对象的属性对象全部删除。

(4)这个复合对象类的操作应该遵守什么约定?这一点需要特殊说明,为了使得本章所
介绍的复合对象类尽可能的简单而且通用,在这里我直接采用可以通过索引号访问自动生
成的变量的代码产生类scatter,这一点可以参见我得“C++自动化(模板元)编程基础与应
用”系列文章。这样之后就可以直接使用许许多多的C++模板元高级技术来实现我们要求的
功能啦:)。

现在问题已经比较明了了,下面看看CODE1的实现和测试代码:

#endif
#ifdef CODE1
#include <iostream>
#include "command.h"
#include "center.h"

//复合对象类的实现
#include "meta.h"//模板元编程基础与应用讨论的内容
#include "identifier.h"//复合对象类必须使用这个类
namespace pandaxcl{
//scatter产生代码需要的基元模板,用来生成属性
template <class T> struct compound_unit
{
//通过下面的代码就可以解释为什么前面在讨论标识号类的时候必须采用
//和类型相关的标识号,而不能够有通用的标识号类了:)
identifier<T> ID;//属性对象的标识号
//在上面的标识号模板中使用默认的第二个模板参数类型long,这一点将
//会在后续的进行弹性相关的讨论的文章中讨论。在这里就是先要让代码
//能够正常运行起来:),通常来说只需要使用默认的参数就可以正常使用
//了,但是对于特殊的情况也不能不进行讨论:)。
};
//利用scatter来生成代码创建自动化类,并通过模板元编程来实现操作代码的自动化
//这样之后这整个类就成了名副其实的自动化类了:)
template<class Cons>class compound
:public scatter<Cons,pandaxcl::compound_unit>
{
public:
typedef Cons cons_type;
typedef scatter<Cons,pandaxcl::compound_unit> scatter_type;
//复合对象的创建函数的自动化实现
template <class ControlType,class CID>
static void create(ControlType&C,const CID&ID)
{
typedef container<CID,compound> TCT;//复合对象容器类型
compound tmp;//定义一个临时的复合类对象
C.record();
//自动化创建所有的子图元
ENVIRONMENT<ControlType> e(C,tmp);
LOOP<CREATE,0,length<cons_type>::value,1>::execute(e);
//然后创建自身,由于create命令和这里的create函数同名,所以
//用名字空间进行限制选择create命令
C.execute(new pandaxcl::create<TCT>(C,ID,tmp));
C.stop();
}
//下面的函数是用来修改复合对象的属性的修改函数
template <size_t i,class PropertyType,class ControlType,class CID>
static void modify(ControlType&C,const CID&ID,const PropertyType&OM)
{
typedef container<CID,compound> TCT;//复合对象容器类型
typedef identifier<PropertyType> PID;//属性对象使用的标识符类型
typedef container<PID,PropertyType> PCT;//属性对象使用的容器类型
typedef compound_unit<PropertyType> UTT;//确保每行代码不至于过长
const compound &S = static_cast<TCT&>(C).reference(ID);
const UTT &P = static_cast<const UTT&>(field<i>(S));
//由于modify命令和这里的midify函数同名,所以用名字空间
//进行限制选择modify命令
C.execute(new pandaxcl::modify<PCT>(C,P.ID,OM));
}
//复合对象的删除函数的自动化实现
template <class ControlType,class CID>
static void remove(ControlType&C,const CID&ID)
{
//因为删除复合对象是一步操作,所以需要将所有的子元素的删除合并
//成为一个复合命令,使之表现的比较象简单对象的删除操作一样。
typedef container<CID,compound> TCT;//复合对象容器类型
const compound&S = static_cast<TCT&>(C).reference(ID);
C.record();
//自动化删除所有的子图元
ENVIRONMENT<ControlType> e(C,S);
LOOP<REMOVE,0,length<cons_type>::value,1>::execute(e);
//然后删除自身,由于remove命令和这里的remove函数同名,所以
//用名字空间进行限制选择remove命令
C.execute(new pandaxcl::remove<TCT>(C,ID));
C.stop();
}
private:
//CREATE和REMOVE都需要传入的动态参数结构
template<class ControlType> struct ENVIRONMENT
{
typed
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
实现QTreeWidget和QTableView支持撤销,需要用到Qt提供的QUndoStack类。QUndoStack是一个用于实现撤销操作的类,它可以在操作之前存储操作状态,并在需要时进行恢复。下面是一个简单的示例代码: ```cpp #include <QApplication> #include <QUndoStack> #include <QUndoCommand> #include <QTreeView> #include <QTableView> #include <QStandardItemModel> class TreeModelCommand: public QUndoCommand { public: TreeModelCommand(QTreeView* treeView, QStandardItemModel* model, int row, QStandardItem* item, QUndoCommand* parent = nullptr): QUndoCommand(parent), m_treeView(treeView), m_model(model), m_row(row), m_item(item) { } void undo() override { m_model->removeRow(m_row); m_treeView->setCurrentIndex(QModelIndex()); } void redo() override { m_model->insertRow(m_row, m_item); m_treeView->setCurrentIndex(m_model->index(m_row, 0)); } private: QTreeView* m_treeView; QStandardItemModel* m_model; int m_row; QStandardItem* m_item; }; class TableModelCommand: public QUndoCommand { public: TableModelCommand(QTableView* tableView, QStandardItemModel* model, int row, int column, const QVariant& data, QUndoCommand* parent = nullptr): QUndoCommand(parent), m_tableView(tableView), m_model(model), m_row(row), m_column(column), m_data(data) { } void undo() override { m_model->setData(m_model->index(m_row, m_column), m_oldData); m_tableView->setCurrentIndex(QModelIndex()); } void redo() override { m_oldData = m_model->data(m_model->index(m_row, m_column)); m_model->setData(m_model->index(m_row, m_column), m_data); m_tableView->setCurrentIndex(m_model->index(m_row, m_column)); } private: QTableView* m_tableView; QStandardItemModel* m_model; int m_row; int m_column; QVariant m_data; QVariant m_oldData; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QUndoStack undoStack; QTreeView treeView; QStandardItemModel treeModel; treeView.setModel(&treeModel); QTableView tableView; QStandardItemModel tableModel; tableView.setModel(&tableModel); QObject::connect(&treeModel, &QStandardItemModel::itemChanged, [&undoStack, &treeView, &treeModel](QStandardItem* item) { auto command = new TreeModelCommand(&treeView, &treeModel, item->row(), item); undoStack.push(command); }); QObject::connect(&tableModel, &QStandardItemModel::dataChanged, [&undoStack, &tableView, &tableModel](const QModelIndex& index, const QVariant& data, const QVariant& oldData) { auto command = new TableModelCommand(&tableView, &tableModel, index.row(), index.column(), data); undoStack.push(command); }); treeModel.appendRow(new QStandardItem("Item 1")); treeModel.appendRow(new QStandardItem("Item 2")); tableModel.setItem(0, 0, new QStandardItem("1")); tableModel.setItem(0, 1, new QStandardItem("One")); tableModel.setItem(1, 0, new QStandardItem("2")); tableModel.setItem(1, 1, new QStandardItem("Two")); treeView.show(); tableView.show(); return a.exec(); } ``` 在上面的示例代码中,我们定义了两个QUndoCommand子类分别用于实现QTreeWidget和QTableView的撤销操作。这两个类都继承自QUndoCommand类,并实现undo()和redo()方法,这两个方法分别用于撤销当前操作。在这两个类的构造函数中,我们保存了操作所需的数据以便于undo()和redo()方法的实现。 在main()函数中,我们创建了一个QUndoStack对象来存储所有操作,并将其与QTreeWidget和QTableView的数据模型的信号连接起来。当QStandardItemModel的itemChanged信号被触发时,我们创建一个TreeModelCommand对象并将其压入undoStack中。当QStandardItemModel的dataChanged信号被触发时,我们创建一个TableModelCommand对象并将其压入undoStack中。 最后,我们通过调用show()方法来显示QTreeWidget和QTableView,并通过调用QApplication的exec()方法启动应用程序。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值