Qt Undo Framework
2014-03-24 17:52
204 查看
本篇主要目的是测试使用sublime text 2 + markdown 发布cnblog
测试结果:非常好用啊有木有!!!所以连文章末尾的logo我都决定给人家保留!!!20130225 鬼猫猫 翻译http://www.cnblogs.com/muyr/
在线阅读:Qt DevDays2007 TheQtUndo-Redo_framework.pdf
下载:Qt DevDays2007 TheQtUndo-Redo_framework.pdf
DevDays2007
Qt undo/redo 框架
基于Command设计模式支持命令压缩和命令合成
提供了与工具包其他部分融合很好的widgets和actions
术语(Terminology)
Command - 对文档的一个作用行为,比如图像编辑器的模糊操作
文本处理器的剪切操作
采样编辑器的最大化操作
Undo-stack - commands的堆栈
Document - 被应用程序编辑的内部数据,比如
音频编辑器中的waveform(波形)
图像编辑器中的bitmap(位图)
基本的undo stack操作
Push![](http://images.cnitblog.com/blog/595709/201403/241742232017423.png)
Undo
![](http://images.cnitblog.com/blog/595709/201403/241742238738052.png)
Redo
![](http://images.cnitblog.com/blog/595709/201403/241742247014165.png)
注意,push可能会删掉一些操作,如图
![](http://images.cnitblog.com/blog/595709/201403/241742252327836.png)
类们
只有四个类!QtUndoCommand - 用于修改document的对象的基类
QtUndoStack - QtUndoCommand对象的堆栈
QtUndoGroup - undo堆栈的组。很多应用程序允许用户同时打开超过一个文档,该类允许你把一组undo堆栈按一单个stack对待。
QtUndoView - 继承自QListWidget,用来展示undo堆栈的内容,以字符串形式
实例
前提说明:下面这个例子,我们将为一个文本编辑器实现undo/redo;文档我们就用一个简单的QString来代表;我们先实现文档中插入字符这样一个commandcommands的实现
插入字符操作class InsertChars : public QUndoCommand { public: InsertChars(int index, const QString &chars, QString *document) : QUndoCommand("Insert characters") { m_index = index; m_chars = chars; m_document = document; } virtual void redo() { m_document->insert(m_index, m_chars); } virtual void undo() { m_document->remove(m_index, m_chars.length()); } private: int m_index; QString m_chars; QString *m_document; };
删除字符操作
class RemoveChars : public QUndoCommand { public: RemoveChars(int index, int count, QString *document) : QUndoCommand("Remove characters") { m_index = index; m_count = cout; m_document = document; } virtual void redo() { m_removedChars = m_document->mid(m_index, m_count); m_document->remove(m_index, m_count); } virtual void undo() { m_document->insert(m_index, m_removedChars); } private: int m_index, m_count; QString m_removedChars; QString *m_document; };
在文本编辑器中使用
MyEditor::MyEditor(QWidget *parent) : QWidget(parent) { // … m_document = new QString; m_stack = new QUndoStack(this); m_toolBar->addAction(m_stack->createUndoAction); m_toolBar->addAction(m_stack->createRedoAction); // … } void MyEditor::keyPressEvent(QKeyEvent *event) { QString chars = events->text(); int index = cursorIndex(); switch (event->key()) { case Qt::Key_Backspace: if (index > 0) m_stack->push(new RemoveChars(index-1, 1, m_document)); break; case Qt::Key_Delete: if (index < m_document.length()) m_stack->push(new RemoveChars(index, 1, m_document)); break; default: if (!chars.isEmpty()) m_stack->push(new InsertChars(index, chars, m_document)); break; } }
command的压缩(compression)
命令压缩,是一种把若干个commands压成一个command的行为。典型的案例就是文本编辑器中输入一大堆文字,撤销,把这一大堆都撤销了。
![](http://images.cnitblog.com/blog/595709/201403/241742263575392.png)
主要用到了QUndoCommand的id()和mergeWith()方法。代码如下
static const int InsertCharsId = 1000; static const int RemoveCharsId = 1001; //... int InsertChars::id() const { return InsertCharsId; } bool InsertChars::mergeWith(const QCommand *command) { // 该类型转换是安全的,因为stack检查过id()了 InsertChars *other = static_cast<InsertChars* > (command); // 只有当其他插入的字符在我的字符后面时,才merge if (m_index + m_chars.length() != other->m_index) return false; // 把它merge了 m_chars.append(other->m_chars); return true; }
command的合成(composition)
也就是传说中的宏(macros)通过合并一系列简单的commands,从而创建复杂的commands
主要是用到了QUndoStack的beginMacro()和endMacro()方法。代码如下
void MyEditor::replace(const QString &oldChars, const QString &newChars) { if(!m_document->contains(oldChars)) return; QString title = QString("Replace '%1' with '%2'").arg(oldChars).arg(newChars); m_stack->beginMacro(title); int index = 0; for(;;) { index = m_document->indexOf(oldChars, index); if(index == -1) break; m_stack->push(new RemoveChars(index,oldChars.length(), m_document)); m_stack->push(new InsertChars(index, newChars, m_document)); index += newChars.length(); } m_stack->endMacro(); }
高级command合成
你大部分的需要,beginMacro()和endMacro()都能充分满足。每个command可以有很多子commands
通过添加子command,构成一个复杂的command
自定义的command合成有很大益处,你可以在push到stack之前,逐步构建command
合成命令的undo顺序如下
![](http://images.cnitblog.com/blog/595709/201403/241742274045206.png)
合成命令的redo顺序如下
![](http://images.cnitblog.com/blog/595709/201403/241742287486462.png)
QUndoCommand* MyEditor::createReplaceCommand(const QString &oldChars, const QString &newChars) { QUndoCommand *replaceCommand = new QUndoCommand(QString("Replace '%1' with '%2'").arg(oldChars).arg(newChars)); int offset = 0; int index = 0; for(;;) { index = m_document->indexOf(oldChars, index); if (index == -1) break; new RemoveChars(index + offset, oldChars.count(), m_document, replaceCommand); new InsertChars(index + offset, newChars, m_document, replaceCommand); index += newChars.cout(); offset += newChars.count() - oldChars.cout(); } return replaceCommand; }
QUndoGroup
一个应用程序,一般有若干个打开的文档,每个都拥有他们自己的undo stack。这些undo stack们可以放到一个undo group里
该组group里的stack可以使用QUndoStack的setActive
()方法将自己设置为active stack。
在同一时间,只能有一个stack是active的。
void MyEditor::MyEditor(QWidget *parent) : QWidget(parent) { //... m_undoGroup = new QUndoGroup(this); m_toolBar->addAction(m_undoGroup->createUndoAction(this)); m_toolBar->addAction(m_undoGroup->createRedoAction(this)); //... } Document *MyEditor::createDocument() { Document *doc = new Document(this); m_documents.append(doc); m_undoGroup->addStack(doc->undoStack()); return doc; } bool Document::event(QEvent *event) { if( event->type() == QEvent::WindowActivate) m_undoStack->setActive(true); // .. return QWidget::event(event); }
Tips
按照commands来设计实现你的应用程序功能---后期很难增加undo/redoUndo commands不应该储存指向document中实际对象的指针---储存其拷贝或者储存足够必要的用于重创建新对象的信息
如果你非得想让commands里储存指向document中对象的指针时,你必须做到如下:
当这些对象在document中被删除的时候,获得对象的多有权
当该command实例被销毁时,delete掉你拥有的那个对象
如果你十分渴望能改变或者移除stack里已经被push的command的话,你很可能会犯以下错误:
你尝试在不只一个文档的情况下使用一个undo堆栈来代表。(原文You are trying to use one undo stack for something that needs to be represented as more than one document)
你的command不是atomic(应该就是说该命令是有若干命令合成或压缩的,不是最最基本的命令)
当命令修改了文档,立马更新该文档的state,使用QUndoStack的indexChanged()信号
该更新信号不应该从command里发射。
Powered by Sublog
相关文章推荐
- 利用Qt Undo FrameWork解决数据回滚问题(撤销恢复)
- qt‘s Undo Framework
- Qt Undo Framework Demo
- QT实例之二十(undo)
- Qt动画框架The Animation Framework
- Qt 学习之路:Graphics View Framework
- 使用Qt Installer Framework制作软件安装包
- ROS in Qt framework
- TreeFrog Framework开发环境搭建 (C++ web framework, based on Qt)
- Java平台下使用QT的The Graphics View Framework
- Qt学习笔记-----Graphics_View_Framework
- Windows程序安装包制作——Qt Installer Framework
- QT Installer Framework 关于组件的问题
- Qt Graphics View Framework(1)---简单使用
- Qt Graphics View Framework 图形视图框架
- Qt's Undo Framework
- Qt的Model/View Framework解析 转载
- QT5 学习之路30---Graphics View Framework
- QT图形视图框架(The Graphics View Framework)
- qdjango - QDjango, a Qt-based C++ web framework - Google Project Hosting