数据模型子类化参考


数据模型子类需要提供 QAbstractItemModel基类定义的需要一些虚函数的实现,需要实现的虚函数数量取决于模型类型,是提供给视图一个简单列表,还是复杂层次的数据项。从 QAbstractListModelQAbstractTableModel可以充分利用基类提供的默认实现。一些需要将数据按照树状结构显示的模型必须提供 QAbstractItemModel基类定义的虚函数。

在数据模型实例中需要实现的函数可以分为三组:

  • Item Data Handling:所有模型需要实现使能视图和代理查询模型尺寸、检查数据项和获取数据的函数;
  • Navigation and index creation:层次模型需要提供视图可以调用函数遍历树状结构,并且获取数据项的模型索引;
  • Drag and drop support and MIME type handling:数据模型继承控制内部和外部拖拽操作函数。这些函数允许数据项按照MIME类型描述,以便方便其他应用程序可以理解数据。

数据项处理

数据模型提供了不同的数据访问层次:可以是简单的只读组件,也可以是支持缩放操作,或者允许数据项被编辑。

只读访问

为了提供对模型数据的只读访问,下面的函数必须在模型子类中实现:

  • flags():其他组件使用该函数获取模型每个数据项的信息。在许多模型中,flags组合应该包括Qt::ItemsEnabledQt::ItemsSelectable
  • data():用于为视图和代理提供数据项。通常,模型仅仅需要为Qt::Display和其他应用相关的用户角色提供数据,但是为Qt::ToolTipRoleQt::AccessibleTextRoleQt::AccessibleDescriptionRole提供数据也是一种好的做法;
  • headerData():为视图提供显示表头的信息。视图获取这些信息后,仅仅用于显示表头信息;
  • rowCount():提供模型显示的数据项行数、

所有类型的数据模型必须实现上述4个函数,包括列表模型(QAbstractListModel基类的子类)和表模型(基类QAbstractTableModel的子类)。
此外,从基类QAbstractTableModelQAbstractItemModel直接继承的子类必须额外实现下面的函数:

  • columnCount():提供模型显示数据的列数。列表模型不提供这个函数,因为列表模型已经在父类QAbstractListModel实现。

可编辑数据项

可编辑模型允许数据项被修改,同时也提供了行列插入和移出的函数。为了使能编辑,下面的函数必须实现:

  • flags():函数必须为每个数据项返回合适的标志组合。尤其是,该函数返回的值必须包括了除适用于只读模型外的Qt::ItemIsEditable
  • setData():用于修改指定模型索引相关的数据项。为了能够接受由用户接口元素提供的用户输入,该函数必须处理与Qt::EditRole相关的数据。在变更数据项后,模型必须发送DataChanged()信号,把变化通知到其他组件。
  • setHeaderData():用于修改水平和垂直表头信息。在修改数据项后,模型必须发headerDataChanged()信号通知其他组件这种变化。

可缩放模型

所有类型的模型都可以支持行插入和移除。Table模型和层次模型支持列插入和移出。重要的是模型尺寸变化前和变化后,需要通知其他组件这些变化。因此,实现下面的函数允许模型可以缩放,但是实现必须确保调用合适的函数通知附加的视图和代理:

  • insertRows():用于添加新行和数据项到所有类型模型。实现在插入新行到任何底层数据结构前必须调用beginInsertRows(),在执行完插入动作后,调用endInsertRows()函数。
  • removeRows():用于从数据模型移除新行和数据项。实现在移除新行前必须调用beginRemoveRows(),在执行完插入动作后,调用endRemoveRows()函数。
  • insertColumns():用于添加新列和数据项到Table模型和层次模型。实现在插入新列到任何底层数据结构前必须调用beginInsertColumns(),在执行完插入动作后,调用endInsertColumns()函数。
  • removeColumns():用于从数据模型移除列和数据项。实现在移除行前必须调用beginRemoveColumns(),在执行完插入动作后,调用endRemoveColumns()函数。

通常情况下,如果操作执行成功时,这些函数返回True。然而一些特殊情况下,操作可能是部分执行成功的,比如,如果实际插入的行数少于指定的行数,这种情况下,模型应该返回False,用于通知相关组件来处理这种问题。

缩放API实现调用的函数发出的信号提供了机会在任何数据不可得前采取动作。在beginend函数之间的插入和移除操作使得模型可以正确管理一致模型索引。

正常情况下,beginend函数能够通知其他组件底层数据结构发生的变化。对于模型结构更加复杂的变化,比如包括内部结构重组,数据排序或者其他结构变化,那么有必要执行如下操作:

  • layoutAboutToBeChanged()信号
  • 更新描述模型结构的内部数据
  • 使用changePersistentIndexList()函数更新一致索引
  • layoutChanged()信号

这些操作序列可以用于任何结构更新。比如,如果一个模型有2亿行数据项,现在需要移除所有奇数行,也就是说一亿行不连续元素。那么按照上述操作,需要调用1亿次beginRemoveRows()endRemoveRows()函数才能完成操作,但是这显然是没有效率的。相反,这可以通过发送一个single layout变化信号一次性更新所有必要的一致索引。

Lazy Population Of Model Data

模型数据的lazy population可以允许模型信息的请求延后,直到视图真正需要时。

一些模型需要从源端数据源获取数据,或者必须执行耗时操作获取关于数据组织的信息。由于视图通常请求尽可能多的信息,目的是准确显示模型数据,限制返回给视图信息数量是有用的,目的是减少非必要数据请求。

在层次模型中,查找指定数据项的子项数目是一项昂贵的操作,确保模型的rowCount()函数只在必要情况下调用是有用的。在这种情况下,hasChildren()函数可以为视图重新实现提供一种便宜的方式来检查子项是否存在。以QTreeView为例,函数为每个父项做合适的渲染。

导航和模型索引创建

层次模型需要提供视图可以遍历树状结构和获取数据项模型索引的函数。

父项子项

由于曝露给视图的结构由底层数据结构决定的,每个模型子类通过提供下面函数的实现创建自己的模型索引:

  • index():给定父项的模型索引,该函数允许视图和代理访问数据项的子项。如果没有有效子项,相关指定行,列和父模型索引可以找到,函数必须返回QModelIndex()
  • parent():提供与给定子项的父项对应的模型索引。如果模型索引对应着模型的底层数据项,或者如果在模型中没有有效父项,函数必须返回一个无效的模型索引(由``QModelIndex()结构体创建).

上述函数都是用createIndex()工厂函数产生其他组件使用的模型索引,正常情况下,给函数提供唯一的ID,确保模型索引与后续的对应项关联。

拖拽支持和MIME类型处理

模型/视图类支持拖拽操作,为许多应用提供足够的默认行为。然而,定制数据项在拖拽操作时编码方式也是可能的,无论数据项被复制或者移动,以及插入到现有模型的方式。

此外,约定视图类实现特定的行为,这些行为紧密服从现有开发者期待。约定视图部分提供了这种行为的概述。

MIME Data

默认情况下,内置模型和视图使用内部MIME类型(application/x-qabstractitemmodeldatalist)传递模型索引信息。这指定了数据的一系列信息,包括每个数据项的行和数量,每个数据项支持的角色。

使用MIME类型的数据编码可以通过调用QAbstractItemModel::mimeData获取。

当在客制化模型实现拖拽支持时,通过实现如下函数按照指定格式导出数据项:

  • mimeData():函数可以重新实现按照格式返回数据,而不是默认的application/x-qabstractitemmdoeldatalist内部MIME类型。子类可以从基类获取默认QMimeData对象和添加数据到基类。

对于许多模型,按照由MIME类型描述的通用格式提供数据项内容是有用的,比如text/plainimage/png。注意,图片,颜色和HTML文档可以使用QMimeData::setImageData()函数添加到QMimeData对象。

接受拖动数据

当对视图执行拖拽操作时,查询底层模型确定它支持的操作类型和它可以接受的MIME类型。QAbstractItemModel::supportedDropActions()QAbstractItemModel::mimeTypes()函数提供这些信息。不重写由QAbstractItemModel提供的实现支持拷贝操作和数据的默认内部MIME类型。

当数据项被拖拽到视图时,数据通过使用QAbstractItemModel::dropMimeData()的实现将数据插入当前模型。该模型默认实现不会覆盖任何数据,相反,它试图插入数据项作为已有数据项的兄弟姐妹或者子项。

为了充分利用内置MIME类型QAbstractItemModel的默认实现,新模型必须重写如下函数:

  • insertRows():函数使得模型使用由QAbstractItemModel::dropMimeData()提供的现有实现自动插入新数据。
  • insertColumns():函数使得模型使用由QAbstractItemModel::dropMimeData()提供的现有实现自动插入新数据。
  • setData():允许新行和新列设置数据项
  • setItemData():该函数提供更多有效的办法设置新数据项

为了接受其他数据形式,下面函数需要重写:

  • supportedDropActions():用于返回拖动动作组合,显示拖动和模型接受的释放操作的类型;
  • mimeTypes():用于返回MIME类型的列表,这些MIME类型可以被模型解码和处理。通常情况下,支持输入到模型的MIME类型与那些由外部组件使用的数据编码一样;
  • dropMimeData():执行由拖拽操作传输数据的解码,决定了数据在模型中如何设置和行列插入方式。子类如何实现该函数取决于每个模型显示数据的要求。

如果dropMimeData()函数的实现通过行列移除和插入、数据项修改改变了模型的尺寸,那么必须小心确保释放相关信号。在子类中简单调用其他的重写,比如setData()insertColumns()确保模型行为一致。

为了确保拖动操作正常工作,必须重写如下函数从模型中移除数据:

  • removeRows()
  • removeRow()
  • removeColumns()
  • removeColumn()

对于更多关于数据项视图拖拽的信息,请参考Using drag and drop with item views

Convenience视图

Convenience视图(QListWidgetQTableWidgetQTreeWidget)重写默认拖拽功能。比如,由于拖拽数据到QTableWidget单元格的情况很常见,使用传输的数据替换已有内容,底层模型将会设置目标数据项的数据,而不是插入新行和新列到模型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值