Undo/Redo for Qt Tree Model

eryar@163.com

Abstract. Qt contains a set of item view classes that use a model/view architecture to manage the relationship between data and the way it is presented to the user. The separation of functionality introduced by this architecture gives developers greater flexibility to customize the presentation of items, and provides a standard model interface to allow a wide range of data sources to be used with existing item views. Model 3D aided design software such as AVEVA Plant/PDMS, Marine use the architecture to manage the design data source. The article demonstrate the Undo/Redo on the Qt Tree model.

Key Words. Model/View, MVC pattern, Undo/Redo, Tree Model

1. Introduction

现 代稍微大型一点的软件,要处理的数据量通常会比较大。这时就需要有一个唯一的数据源,且会对这个数据源中的数据进行增、删、改的操作。如果没有统一的数据 源,数据会随意地被创建和删除,且创建和删除的用户界面也不统一,不利于软件管理。基于唯一的数据源,并在这个基础上提供统一的增删改接口,不仅有利于软 件数据管理,还有利于事务的处理,即Undo/Redo功能。若引入脚本语言,如Tcl或Python,甚至可实现脚本命令对数据的操作,为程序增加二次 开发功能。

Figure 1. AVEVA Plant/PDMS Software

如 上图1所示为英国剑桥大学CADCENTRE出品的AVEVA Plant/PDMS软件。PDMS使用了统一的数据源,左边的导航树及右边的三维模型都是这个数据源的一种展示方式,可以在导航树上创建及删除数据;也 可以在三维视图中进行交互,方便地创建及修改模型。这种方式与GoF描述的Observer观察者模式相似,即:

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

对应上面的软件,即对于唯一的数据源这个对象,导航树及三维视图都依赖于他。当数据源这个对象中有数据的增删改时,导航树及三维视图都会得到通知并自动更新了。

图1右下角的CommandWindow中可以输入命令,即PML,也可以对数据源进行修改。引入Observer模式,即可实现这些功能。

Qt 中包含了一系列的Model/View类,这些类都是基于MVC架构的,这种将数据与视图分开的处理,使同一个数据源可以不同的视图中进行展示,且不需要 修改数据源的结构。Qt中基于Command命令模式实现了一个Undo/Redo的框架,将Undo/Redo框架应用于Model/View的类,即 可得到与AVEVA Plant/PDMS中类似的功能。

本文主要对Qt中的model/view及undo/redo框架进行介绍,及如何将Undo/Redo框架应用到model/view中去,实现对以层次方式组织数据的树模型进行undo/redo,进而实现一个三维CAD工厂设计的原型。

2.Tree Model

软件中使用层次方式来组织数据,可护展性好。如OpenCASCADE中的OCAF框架的数据也是树形方式组织:

Figure 2.1 OpenCASCADE OCAF data tree

每 个Label中可以存放属性,还包含子Label,这样就提供了一个通用的程序框架,灵活使用,可以用一种统一的方式来存放数据。有一个缺点就是每个 Label中的属性类型只能包含一种,这种方式相对于AVEVA的自定义属性UDA来说,就显得很不方便了,需要提前对数据的存储进行规划,扩展起来稍有 不便。

AVEVA中对每个数据Element都提供了自定义属性(User Definable Attribute),每种类型的属性(数值型、字符串型等)都可以包含任意数量,没有限制。所以可扩展性更好。

Qt 中基于MVC模式的model/view架构提供了一些预定义的模型及视图,如列表模型及对应的视图,树模型及树视图等。在model/view架构中, 为model提供了统一的访问数据的方式,即Model Index。对于列表model,通过QModelIndex::row()即可访问。对于表格model,需要QModelIndex::row()和 QModelIndex::column()。对于树model,除了上述两个函数,还需要提供QModelIndex::parent()来对父节点进 行访问。如下图2.2所示:

Figure 2.2 Schematic view of Qt’s Models

若需要对列表及表格model应用Undo框架,还是很方便的,只要记住数据操作的对应的row或row及column即可。若要对树形model进行undo,原理也是一样的,即在redo中生成数据在undo中恢复数据。

3.Key Points

将undo框架与model/view结合有两种方式:

v 通过在undo/redo命令中调用QAbstractItemModel的API来改变底层数据;

v 自定义model使其创建命令并推送到undo栈Stack中去;

第 一种方式看起来要容易些,且可与任意model结合。The first approach seems simpler, as it works with any model and gives the undo framework total control --- you can create as many different types of commands as you want and each of them can call a number of methods from the model API in their undo() and redo() routines.

当model中有delete操作时,会将index内部的数据删除。对于这种情况,需要注意。Qt给出了两个解决办法:

There are two things to remember. The first is that, if you delete items, rows or columns, you should save the data of all the items getting deleted(including children) somewhere so that you can later revert the deletion. The second is that it would be nice to be able to set meaningful descriptions of commands pushed on the stack. To do that you can provide methods in the model, that are called each time a command is pushed onto the stack, which should each return a description based on the parameters of the command being executed.

基于上述思想,实现了Tree Model与Undo Framework结合的一个小例子:

Figure 3. Undo/Redo and Tree Model

4.Conclusion

Qt中基于Command命令模式的Undo框架,需要对数据操作及恢复仔细考量。尤其对于有删除数据的操作,若以QModelIndex作为参考依据,则会出错。对于此Qt提供了两个解决思路。

基于Qt的model/view及undo框架,也可以方便实现OpenCASCADE中的OCAF框架许多类似的功能,且Qt的代码可读性更高。若要快速开发程序,可以考虑使用Qt的model/view框架。

感谢晓天的建议。

5. References

1. Using Undo/Redo with Item Views. http://doc.qt.digia.com/qq/qq25-undo.html

2. OpenCASCADE. OCAF User’s Guide. 2014

3. GoF. Design Patterns-Element of Reusable Object-Oriented Software. 1995

Undo/Redo for Qt Tree Model的更多相关文章

  1. Qt自定义model

    前面我们说了Qt提供的几个预定义model.但是,面对变化万千的需求,那几个model是远远不能满足我们的需要的.另外,对于Qt这种框架来说,model的选择首先要能满足绝大多数功能的需要,这就是说, ...

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

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

  3. MySQL,MariaDB:Undo | Redo [转]

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  4. iOS: 为画板App增加 Undo/Redo(撤销/重做)操作

    这个随笔的内容以上一个随笔为基础,(在iOS中实现一个简单的画板),上一个随笔实现了一个简单的画板:   今天我们要为这个画板增加Undo/Redo操作,当画错了一笔,可以撤销它,或者撤销之后后悔了, ...

  5. [转]MySQL日志——Undo | Redo

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  6. 从Undo,Redo谈命令模式

    一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作. 要实现上面的这一功能,最直观的想法就是,我们 ...

  7. 【转载】MySQL 日志 undo | redo

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  8. MySQL InnoDB存储引擎undo redo解析

    本文介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo Log Undo Log 为了实现事务原子,在MySQL数据库InnoDB存储引擎,还使用Undo Log(简称:MVCC ...

  9. MySQL日志Undo&Redo

    00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC). - 事务的原子性(Atomi ...

随机推荐

  1. SQLServer CASE WHEN 用法

    SELECT sc.NAME AS 学校名称 ,xueyuan.NAME AS 院系 ,StudentNo AS 学号 ,st.StudentName AS 学生姓名 ,st.sex AS 性别 ,s ...

  2. KMP匹配算法 - Number Sequence

    Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...... , b[M] (1 <= M ...

  3. css3的动画特性

    css3 的过渡动画,有些时候会产生一些其他问题. 比如:你想先改一个css属性,之后再进行动画过渡,结果实际上你会发现,css属性和动画是同步进行的.这个应该是在编译阶段就确定好了. 所以只能通过s ...

  4. 网页制作中在头部固定悬浮table表头(thead)的方法

    这两天接了一个需求,页面是这样的 然后需求是页面中的这个表格当页面向上滚动,且表格的表头到达窗口上方时,表头悬浮在页面的上方,表格正常滚动,这样表格内的数据可以随时看到表头内容. 一开始我认为这是极简 ...

  5. Node.js抓取网页

    前几天四六级成绩出来(然而我没考),用Node.js做了一个模拟表单提交并抓取数据的Web 总结一下用到的知识,简单的网页抓取大概就是这个流程了 发送Get或Post请求 表单提交,首先弄到原网页提交 ...

  6. HTML 上传图片实用小技巧

    最近写的项目需要用的上传图片的功能但是浏览器自带的按钮样式实在是不忍直视,肯定要进行修改,网上也有很多方法(自己查....),我这里用了个取巧的方法:就是函数的间接调用 在点击btn的时候让它执行了图 ...

  7. mysql多字段排序

    在对数据库进行查询的时候有时候需要将查询的结果按照某字段升序或者降序排列,甚至有时候需要按照某两个字段进行升降序排列.如果按照某一字段进行排列,只需要在查询语句最后写上 "order by ...

  8. kgcd ,fmod,fgcd

    参考:NENU CS ACM模板made by tiankonguse  2.13 GCD 快速gcd: 位操作没学,真心不懂二进制,还是得学啊 code: int kgcd(){ if(!a || ...

  9. 用itextsharp打印pdf示例

    学习了用itextsharp输出PDF文档,支持用XML定义文档,可定义多个数据源,简单的表达式,用于项目中效果还不错,其中PdfPageEvent类由包大人提供. 示例程序定义了一个简单的xml文件 ...

  10. sessionStorage/localStorage 本地存储

      if(!sessionStorage.getItem('vote2')||sessionStorage.getItem('vote2')==NaN)sessionStorage.setItem(' ...