Qt Model/View

Model/View

模型层继承结构

QAbstractItemModel的父类:QObject

  • QAbstractItemModel
    • QStandardItemModel
    • QAbstractListModel
    • QAbstractTableModel
    • QAbstrctProxyModel
    • QConcatenateTablesProxyModel
    • QDirModel
    • QFileSystemModel

QAbstractItemModel

**QAbstractItemModel:**定义标准接口,这些接口用于模型数据和其他model/view架构中的其他组件进行数据访问。

如果需要使用List或者Table类型的模型结构,建议继承QAbstractListModel或者QAbstractTableModel,而不推荐使用QAbstractItemModel。

对于模型结构中的item,每一个item都有其对应结构下的QModelIndex,可以通过index()函数来获取该item元素。

每一个index都有一个sibling() index(Qt6移除),每一个子item都有其parent() index。

每一个item都有其对应的role,比如:Qt::ItemDataRole。

可以调用setData()或者setItemData()来指定item的role,具体可查阅API文档。

可以调用flags()来查看item是否可以被selected、dragged或进行其他方式的操作。

可以调用hasChildren()函数来查询对应QModelIndex的item是否有子item。

可以调用rowCount(),columnCount()来查询每一层级上的行列个数,因为items可能以层级结构一层一层进行组织存放。可以调用insertRows()、insertColumns()、removeRows()、removeColumns()来插入或者移除元素。

可以调用match()函数来查找特定的数据。

可以调用sort()函数来对模型数据进行排序。

继承QAbstractItemModel时,至少需要实现index(),parent(),rowCount(),columnCount()和data()函数。

同时当rowCount()的实现比较expensive时,可以重新实现hasChildren()函数来提供特殊的模型的特殊存储方式。通过这种方式,可以约束视图展现数据的多少,达到数据的懒加载

可以实现setData来确保item可以编辑,同时重新实现flags函数来确保这个item的编辑状态(ItemIsEditable)是正常的。也可以实现headerData和setHeaderData函数来控制模型展现数据头的方式。

当重新实现setData()函数和setHeaderData()函数时,必须显示发送(emit) dataChanged()或者headerDataChanged()信号。

对于自定义模型,必须创建模型索引来提供数据访问,可以调用createIndex()函数来为item指定对应的QModelIndex。

模型底层数据结构的重定义:

  • insertRows():在插入新行到数据结构之前需要调用beginInsertRows(),在插入结束立马调用endInsertRows()
  • insertColumns():同上需要调用beginInsertColumns()和endInsertColumns()
  • removeRows():同上需要调用beginInsertRows()和endInsertRows()
  • removeColumns():同上需要调用beginRemoveColumns()和endRemoveColumns()

在自定义模型的底层数据存取形式时,调用beginXXX()和endXXX()函数的原因是保证数据持久化正常。

如果对于item本身来说,其下面还有层级结构的数据,Qt所提供的方式是会对item进行insert或者remove时,Qt会自动管理item下层的数据结构。

QStandardItemModel

Item的基类:QStandardItem

实现了QAbstractItemModel接口,对于QListView、QTableView、QTreeView或者自定义实现的这些View类(这些View内部都使用了QAbstractItemModel接口),他们可以直接使用QStandardItemModel或者其子类来作为自己的模型数据。为了性能和灵活性考虑,可以使用QAbstractItemModel来提供更加多样性的数据展现形式。

当想要一个list或者tree,可以创建一个空的QStandardItemModel,并使用appendRow()函数来增加数据到model中,然后调用item()函数来对item进行访问。如果想要model来以table的方式存储数据,可以将维度作为参数传导QStandardItemModel的构造函数中,然后调用setItem函数来讲item设置到特定的位置。也可以调用setRowCount或者setColumnCount函数来修改模型的维度。可以调用insertRows和insertColumn来插入item,调用removeRows和removeColumns来移除元素。可以调用setHorizontalHeaderLabels和setVerticalHeaderLabels来设置模型的头标签。可以调用findItems来查找元素,也可以调用sort函数来对模型数据进行排序。调用clear函数来移除model中的所有item。

QAbstractListModel

介绍

存储那种简单的没有继承序列item的结构,不能直接使用,必须继承以供使用。

由于QAbstractListModel相较于QAbstractItemModel有了更特定的接口函数,因此不适合在需要非List模型数据的view中使用,比如tree views。

继承

继承QAbstractListModel,必须实现rowCount()函数和data(),比较合理的实现是还是需要提供headerData()的实现。

如果对于可编辑的list model,必须提供setData的实现,同时实现flags(),如此便可以返回Qt::ItemIsEditable。

Note:QAbstractListModel提供了一个columnCount()函数的默认的实现,可以告知views:只有一个单列的items在模型中。

提供接口insertRows()和removeRows()来向模型中插入数据,使用前必须调用beginXXX()和endXXX()来确保数据的持久化存储正确。

QAbstractTableModel

介绍

类似于QAbstractTableModel,只是数据的存储维度不一样。

继承

类似需要的实现和QAbstractTableModel差不太多。

QAbstractProxyModel

介绍

定义了标准接口使得代理模型可以用来model/view组件进行正确的交互操作。不支持直接初始化。

Qt中已有的实现的所有的标准代理模型继承自QAbstractProxyModel类,如果需要创建新的代理模型类,则需要继承一个已存在的代理类并且提供所想需要表达的函数接口来提供想要的行为表示。

用代理模型来对model中的items进行过滤或者排序需要内部包含或者继承QSortFilterProxyModel类,内部包含或者继承属于两种使用方式,可以酌情使用。

为了继承QAbstractProxyModel,需要实现mapFromSource()和mapToSource()接口。接口mapSelectionFromSource()和mapSelectionToSource()函数只需要在需要提供不同于默认行为的行为时才进行相应的实现。

QDirModel

不再建议使用

QFileSystemModel

介绍

该类提供了接口函数来访问本地文件系统,提供函数来重命名和移除文件和目录,创建目录等。在简单的案例中,该类可以作为浏览器或者过滤器组件的一部分。

QFileSystemModel可以使用QAbstractItemModel中的接口函数进行访问(因为由上面的继承结构可以看出:QFileSystemModel是从QAbstractItemModel继承来的),同时也提供了另外特定的接口函数来访问目录模型。fileInfo(),isDir(),fileName()和filePath()提供底层文件和目录模型结构的信息。可以调用mkdir()、rmdir()函数来创建或者移除目录。

Example

// 创建模型,指定根目录
QFileSystemModel* model=new FileSystemModel;
model->setRootPath(QDir::currentPath());

// 将模型和当前创建的Tree视图进行绑定
QTreeView* tree=new QTreeView(splitter);
tree->setModel(model);

// 设置根索引
tree->setRootIndex(model->index(QDir::currentPath()));

QConcatenateTablesProxyModel

介绍

QConcatenateTablesProxyModel占据多个模型并连接这多个模型的rows。

换句话说,代理会拥有第一个原模型的所有rows,接下去是第二个模型的rows,依此类推。

如果各个model的列数不一样,那么代理会按照最小列数,对模型进行连接。(那么我想,这种模型数据结构在什么业务场景下会有效???)其他多余的列会在连接的时候被忽略。

model可以在运行时被增加或者删除,列的数量会自动适应。

QStyledItemDelegate

介绍

当在Qt的views中展现model中的数据时,Qt使用delegate来对items进行绘制。当一个item是edited,Qt会为这个item提供editor widget。当编辑事件发生时,这个widget会放在对应的view对象的top位置。QStyledItemDelegated是Qt所有views的默认的delegate,在这些views被创建时就自动绑定了QStyledItemDelegate对象。

Qt引入delegate机制的原因是:将item的作用(编辑、展示

或者其他)、view展示model中的item这种业务逻辑相拆分,从而降低Qt组件间的耦合度,同时也方便了自定义model/view中的组件们。

model中的item数据都有一个ItemDataRole的属性变量,用来表征item的行为属性。每一个item可以为每一个role存储一个QVariant(Qt的一种变量类型)变量值。QStyledItemDelegate实现了程序员所需的绝大多数公共的数据类型(eg. booleans,integers,strings…)用来对item进行display和editing。

view会根据model中的item的role来决定item在view中的展现方式。

以下列举常用的Role和其对应的可以接受的数据类型:

Role Accepted Types
Qt::BackgroundRole QBrush
Qt::BackgroundColor QColor(obsolete;use Qt::BackgroundRole instead)
Qt::CheckStateRole Qt::CheckState
Qt::DecorationRole QIcon,QPixmap,QImage,QColor
Qt::DisplayRole QString and types with a string representation
Qt::EditRole See QItemEditorFactory for details
Qt::FontRole QFont
Qt::SizeHintRole QSize
Qt::TextAlignmentRole Qt::Alignment
Qt::ForegroundRole QBush
Qt::TextColorRole QColor(obsolete; use Qt::ForegroundRole instead)

对于Qt::EditRole做一点补充:Editors可以用QItemEditorFactory来创建,QItemEditor提供了一个静态实例,该实例默认被install到所有item的delegate上。程序员可以使用setItemEditorFactory()来设置一个自定义的工厂或者使用QItemEditorFactory::setDefaultFactory()来设置一个新的默认工厂对象。

继承QStyledItemDelegate

当delegate不支持所需绘制的数据类型或者程序员项自定义item的绘制方式,则可以通过继承QStyledItemDelegate的方式,继承后需要实现paint()、sizeHint()函数。需要为每个item调用paint()函数以获得不同的展示方式。通过sizeHint()函数,可以指定每一个item的hint(其实就是QSize)。

在需要使用一个自定义的delegate,其中的元素的role又具有edit属性时,除了使用之前提到的使用QItemEditorFactory的方式之外,可以采取不使用的方式,但是必须重新实现以下四个方法:

  • createEditor(): 返回模型中用来更改数据的widget,可以重新实现来自定义编辑行为
  • setEditorData(): 提供带有数据的widget以便操作
  • updateEditorGeometry(): 确保对于特定的view,editor控件能够正确展示
  • setModelData(): 将更新过后的数据返回给模型

具体实例可以参照 Qt Star Example

你可能感兴趣的:(Qt基础)