Overview of Qt's Undo Framework

Introduction

Qt's Undo Framework is an implementation of the Command pattern, for implementing undo/redo functionality in applications.

The Command pattern is based on the idea that all editing in an application is done by creating instances of command objects. Command objects apply changes to the document and are stored on a command stack. Furthermore(此外), each command knows how to undo its changes to bring the document back to its previous state. As long as the application only uses command objects to change the state of the document, it is possible to undo a sequence of commands by traversing the stack downwards and calling undo on each command in turn. It is also possible to redo a sequence of commands by traversing the stack upwards and calling redo on each command.

Classes

The framework consists of four classes:

  1. QUndoCommand is the base class of all commands stored on an undo stack. It can apply (redo) or undo a single change in the document.
  2. QUndoStack is a list of QUndoCommand objects. It contains all the commands executed on the document and can roll the document's state backwards or forwards by undoing or redoing them.
  3. QUndoGroup is a group of undo stacks. It is useful when an application contains more than one undo stack, typically one for each opened document. QUndoGroup provides a single pair of undo/redo slots for all the stacks in the group. It forwards undo and redo requests to the active stack, which is the stack associated with the document that is currently being edited by the user.
  4. QUndoView is a widget which shows the contents of an undo stack. Clicking on a command in the view rolls the document's state backwards or forwards to that command.

Concepts

The following concepts are supported by the framework:

  • Clean state: Used to signal when the document enters and leaves a state that has been saved to disk. This is typically used to disable or enable the save actions, and to update the document's title bar.
  • Command compression: Used to compress sequences of commands into a single command. For example: In a text editor, the commands that insert individual characters into the document can be compressed into a single command that inserts whole sections of text. These bigger changes are more convenient for the user to undo and redo.
  • Command macros: A sequence of commands, all of which are undone or redone in one step. These simplify the task of writing an application, since a set of simpler commands can be composed into more complex commands. For example, a command that moves a set of selected objects in a document can be created by combining a set of commands, each of which moves a single object.

QUndoStack provides convenient undo and redo QAction objects that can be inserted into a menu or a toolbar. The text properties of these actions always reflect what command will be undone or redone when they are triggered. Similarly, QUndoGroup provides undo and redo actions that always behave like the undo and redo actions of the active stack.

QUndoCommand

The QUndoCommand class is the base class of all commands stored on a QUndoStack.

A QUndoCommand represents a single editing action on a document; for example, inserting or deleting a block of text in a text editor. QUndoCommand can apply a change to the document with redo() and undo the change with undo(). The implementations for these functions must be provided in a derived class.

class AppendText : public QUndoCommand
{
public:
AppendText(QString *doc, const QString &text)
: m_document(doc), m_text(text) { setText("append text"); }
virtual void undo()
{ m_document->chop(m_text.length()); }
virtual void redo()
{ m_document->append(m_text); }
private:
QString *m_document;
QString m_text;
};

A QUndoCommand has an associated text(). This is a short string describing what the command does. It is used to update the text properties of the stack's undo and redo actions; see QUndoStack::createUndoAction() and QUndoStack::createRedoAction().

QUndoCommand objects are owned by the stack they were pushed on. QUndoStack deletes a command if it has been undone and a new command is pushed. For example:

MyCommand *command1 = new MyCommand();
stack->push(command1);
MyCommand *command2 = new MyCommand();
stack->push(command2); stack->undo(); MyCommand *command3 = new MyCommand();
stack->push(command3); // command2 gets deleted

In effect, when a command is pushed, it becomes the top-most command on the stack.

To support command compression, QUndoCommand has an id() and the virtual function mergeWith(). These functions are used by QUndoStack::push().

To support command macros, a QUndoCommand object can have any number of child commands. Undoing or redoing the parent command will cause the child commands to be undone or redone. A command can be assigned to a parent explicitly in the constructor. In this case, the command will be owned by the parent.

The parent in this case is usually an empty command, in that it doesn't provide its own implementation of undo() and redo(). Instead, it uses the base implementations of these functions, which simply call undo() or redo() on all its children. The parent should, however, have a meaningful text().

QUndoCommand *insertRed = new QUndoCommand(); // an empty command
insertRed->setText("insert red text"); new InsertText(document, idx, text, insertRed); // becomes child of insertRed
new SetColor(document, idx, text.length(), Qt::red, insertRed); stack.push(insertRed);

Another way to create macros is to use the convenience functions QUndoStack::beginMacro() and QUndoStack::endMacro().

QUndoStack

The QUndoStack class is a stack of QUndoCommand objects.

An undo stack maintains a stack of commands that have been applied to a document.

New commands are pushed on the stack using push(). Commands can be undone and redone using undo() and redo(), or by triggering the actions returned by createUndoAction() and createRedoAction().

QUndoStack keeps track of the current command. This is the command which will be executed by the next call to redo(). The index of this command is returned by index(). The state of the edited object can be rolled forward or back using setIndex(). If the top-most command on the stack has already been redone, index() is equal to count().

QUndoStack provides support for undo and redo actions, command compression, command macros, and supports the concept of a clean state.

Undo and Redo Actions

QUndoStack provides convenient undo and redo QAction objects, which can be inserted into a menu or a toolbar. When commands are undone or redone, QUndoStack updates the text properties of these actions to reflect what change they will trigger. The actions are also disabled when no command is available for undo or redo. These actions are returned by QUndoStack::createUndoAction() and QUndoStack::createRedoAction().

Clean State

QUndoStack supports the concept of a clean state. When the document is saved to disk, the stack can be marked as clean using setClean(). Whenever the stack returns to this state through undoing and redoing commands, it emits the signal cleanChanged(). This signal is also emitted when the stack leaves the clean state. This signal is usually used to enable and disable the save actions in the application, and to update the document's title to reflect that it contains unsaved changes.

Obsolete Commands

QUndoStack is able to delete commands from the stack if the command is no longer needed. One example may be to delete a command when two commands are merged together in such a way that the merged command has no function. This can be seen with move commands where the user moves their mouse to one part of the screen and then moves it to the original position. The merged command results in a mouse movement of 0. This command can be deleted since it serves no purpose. Another example is with networking commands that fail due to connection issues. In this case, the command is to be removed from the stack because the redo() and undo() functions have no function since there was connection issues.

A command can be marked obsolete(过时的) with the QUndoCommand::setObsolete() function. The QUndoCommand::isObsolete() flag is checked in QUndoStack::push(), QUndoStack::undo(), QUndoStack::redo(), and QUndoStack::setIndex() after calling QUndoCommand::undo(), QUndoCommand::redo() and QUndoCommand:mergeWith() where applicable.

If a command is set obsolete and the clean index is greater than or equal to the current command index, then the clean index will be reset when the command is deleted from the stack.

QUndoGroup

The QUndoGroup class is a group of QUndoStack objects.

An application often has multiple undo stacks, one for each opened document. At the same time, an application usually has one undo action and one redo action, which triggers undo or redo in the active document.

QUndoGroup is a group of QUndoStack objects, one of which may be active. It has an undo() and redo() slot, which calls QUndoStack::undo() and QUndoStack::redo() for the active stack. It also has the functions createUndoAction() and createRedoAction(). The actions returned by these functions behave in the same way as those returned by QUndoStack::createUndoAction() and QUndoStack::createRedoAction() of the active stack.

Stacks are added to a group with addStack() and removed with removeStack(). A stack is implicitly added to a group when it is created with the group as its parent QObject.

It is the programmer's responsibility to specify which stack is active by calling QUndoStack::setActive(), usually when the associated document window receives focus. The active stack may also be set with setActiveStack(), and is returned by activeStack().

When a stack is added to a group using addStack(), the group does not take ownership of the stack. This means the stack has to be deleted separately from the group. When a stack is deleted, it is automatically removed from a group. A stack may belong to only one group. Adding it to another group will cause it to be removed from the previous group.

A QUndoGroup is also useful in conjunction with QUndoView. If a QUndoView is set to watch a group using QUndoView::setGroup(), it will update itself to display the active stack.

QUndoView`

The QUndoView class displays the contents of a QUndoStack.

QUndoView is a QListView which displays the list of commands pushed on an undo stack. The most recently executed command is always selected. Selecting a different command results in a call to QUndoStack::setIndex(), rolling the state of the document backwards or forward to the new command.

The stack can be set explicitly with setStack(). Alternatively, a QUndoGroup object can be set with setGroup(). The view will then update itself automatically whenever the active stack of the group changes.

Demos

  1. undoframework
  2. undo

Reference

Qt Help manual


Qt's Undo Framework的更多相关文章

  1. Qt Undo Framework Demo

    Qt Undo Framework Demo eryar@163.com Abstract. Qt’s Undo Framework is an implementation of the Comma ...

  2. Qt Undo Framework

    Qt undo/redo 框架 基于Command设计模式 支持命令压缩和命令合成 提供了与工具包其他部分融合很好的widgets和actions 术语(Terminology) Command - ...

  3. qt Graphics View Framework(非重点)

    Graphics View 提供了一种接口,用于管理大量自定义的 2D 图形元素,并与之进行交互:还提供了用于将这些元素进行可视化显示的观察组件,并支持缩放和旋转. 说明;Graphics View ...

  4. 很多人以为 connect 和 disconnect 应该像 new 和 delete 一样成对出现 这是错误的(只要 sender 或 receiver 其中之一不存在了,connect 会自动失效。QObject::connect 函数是线程安全的)

    其实我写文章也是边查资料边编辑的 有时候是怕自己的阐述不严谨,有时候是怕自己重复造轮子 就像有些人不停的教大家QLabel QDialog QWidget 个人是不屑的 命令模式 用 Qt's Und ...

  5. Undo/Redo for Qt Tree Model

    Undo/Redo for Qt Tree Model eryar@163.com Abstract. Qt contains a set of item view classes that use ...

  6. Qt: The State Machine Framework 学习

    State Machine,即为状态机,是Qt中一项非常好的框架.State Machine包括State以及State间的Transition,构成状态和状态转移.通过状态机,我们可以很方便地实现很 ...

  7. Qt的Model/View Framework解析(数据是从真正的“肉(raw)”里取得,Model提供肉,所以读写文件、操作数据库、网络通讯等一系列与数据打交道的工作就在model中做了)

    最近在看Qt的Model/View Framework,在网上搜了搜,好像中文的除了几篇翻译没有什么有价值的文章.E文的除了Qt的官方介绍,其它文章也很少.看到一个老外在blog中写道Model/Vi ...

  8. qt QUndoGroup的使用

    最近项目中用到撤销,恢复功能.qt的demo中有一个例子,是类似于单文档的.而我的项目中是类似于多文档的项目,即可能要打开多个页面,不同的页面都有撤销恢复功能.这样的话,就要用到QUndoGroup类 ...

  9. Qt Examples Qt实例汇总

    ActiveQt Examples Using ActiveX from Qt applications. Animation Framework Examples Doing animations ...

随机推荐

  1. 《DSP using MATLAB》示例 Example 9.11

    代码: %% ------------------------------------------------------------------------ %% Output Info about ...

  2. java编程之常见的排序算法

    java常见的排序算法 第一种:插入排序 直接插入排序 1, 直接插入排序 (1)基本思想:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排 好顺序的,现在要把第n个数插到前面的 ...

  3. python爬虫——多线程+协程(threading+gevent)

    上一篇博客中我介绍了如何将爬虫改造为多进程爬虫,但是这种方法对爬虫效率的提升不是非常明显,而且占用电脑cpu较高,不是非常适用于爬虫.这篇博客中,我将介绍在爬虫中广泛运用的多线程+协程的解决方案,亲测 ...

  4. Hive之 数据存储

    首先,Hive 没有专门的数据存储格式,也没有为数据建立索引,用户可以非常自由的组织 Hive 中的表,只需要在创建表的时候告诉 Hive 数据中的列分隔符和行分隔符,Hive 就可以解析数据. 其次 ...

  5. SQL中利用脚本创建database mail.

    SQL中利用脚本创建database mail   编写人:CC阿爸 2014-6-14 多话不讲,请参考以下脚本 use  

  6. Java反射机制的适用场景及其利与弊 ***

    一.反射的适用场景是什么? 1).Java的反射机制在做基础框架的时候非常有用,有一句话这么说来着:反射机制是很多Java框架的基石.而一般应用层面很少用,不过这种东西,现在很多开源框架基本都已经给你 ...

  7. linux Posix 信号量 一

    信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语. linux提供两种信号量,“内核信号量”和“用户态进程信号量”,“用户态信号量”又分为“Posix”,“System V”信号 ...

  8. Devexpress TreeList 展开和折叠当前选中节点

    1.展开: private void barButtonItem6_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) ...

  9. mysql 5.6.15升级到5.6.43

    今天闲来无事,观察测试环境的zabbix服务器,发现内存泄漏严重,于是重启了,想起了前几天写的帖子发生了严重的内存泄漏可以把mysql升级到最新的小版本 于是乎就试着升级 old version:5. ...

  10. 寒武纪-1005 Travel(树形DP)

    一.题目链接 http://aiiage.hustoj.com/problem.php?id=1005 二.题面 PDF:http://aiiage.hustoj.com/upload/file/20 ...