StupidBeauty
Read times:205Posted at:Tue Aug 10 20:59:03 2010 - no title specified

Qt4.6.2文档翻译:创建新的模型

  • •.•.介绍

  • •.•.设计一个模型

  • •.•.一个只读的示例模型

    • .•.模型的维度

    • .•.模型表头和数据

  • •.•.一个可编辑的模型

    • .•.使模型成为可编辑的

    • .•.插入和删除行

  • •.•.接下来的步骤

介绍

模型/视图组件之间功能的分割允许创建那些能够利用现有的视图的模型。这种允许我们使用像QListView、QTableView和QTreeView这样的标准图形界面组件来展示来自不同数据源的数据。

QAbstractItemModel类提供一个足够支持将信息组织在层次型结构中的数据源的灵活的接口,这样就允许数据以某种方式被插入、删除、修改和排序。它也提供对拖放操作的支持。

QAbstractListModel和QAbstractTableModel类提供对更简单的非层次型数据结构的接口的支持,它们更易于用来作为一个简单的列表和表格模型的开始点。

在这一章中,我们创建一个简单的只读的模型,以探索模型/视图架构中的基本原则。在本章随后的地方,我们将会调整这个简单的模型,以让项目可以被用户修改。

要看一个更复杂的模型的例子,参看简单树模型示例。

QAbstractItemModel的子类所要满足的条件在模型子类化参考文档中更细致地描述。

设计一个模型

当 你为一个已经存在的数据结构创建一个新的模型时,很重要的一点是,需要考虑应当使用哪种模型来为数据提供接口。如果该数据结构可以被表示为一个由项目组成 的列表或者表格,那么你可以子类化QAbstractListModel或者QAbstractTableModel,因为这些类为很多函数提供了合适的 默认实现。

然而,如果底层的数据结构只能被当成一个层次性的树型结构来表示,则必须对QAbstractItemModel进行子类化。在简单树模型示例中是使用这种方法。

在这一章中,我们将会实现一个基于字符串列表的简单模型,所以QAbstractListModel提供了一个理想的基类。

无 论底层数据结构是什么样的形式,一般情况下,将一个允许更自然地访问底层数据结构的应用编程接口补充到标准的QAbstractItemModel应用编 程接口中,是一个好主意。这使得用数据来填充模型的过程更简单,同时仍然允许其它通用的模型/视图组件使用标准应用编程接口来与模型交互。下面描述的模型 提供了一个自定义的构造函数,就只是为了这个目的。

一个只读的示例模型

这里实现的模型是一个简单的、非层次性的、只读的 数据模型,基于标准的QStringListModel类。它使用一个QStringList作为它的内部数据源,并且只实现一个正常工作的模型所需要的 函数。为了使得实现过程更简单,我们对QAbstractListModel子类化,因为它定义了对于列表模型有意义的默认行为,而且它展示出一个比 QAbstractItemModel类要简单的接口。

在实现一个模型时,有一点很重要,就是记住QAbstractItemModel自己并不存储任何数据,它只是展示出一个由视图用来访问数据的接口。对于一个最小化的只读的模型,只需要实现很少的一些函数,因为大部分接口都有默认的实现了。类的声明如下:

class StringListModel : public QAbstractListModel

{

Q_OBJECT

public :

StringListModel ( const QStringList &strings, QObject *parent = 0 )

: QAbstractListModel(parent), stringList (strings) {}

int rowCount( const QModelIndex &parent = QModelIndex()) const ;

QVariant data( const QModelIndex &index, int role) const ;

QVariant headerData( int section, Qt::Orientationorientation,

int role = Qt::DisplayRole) const ;

private :

QStringList stringList ;

};

除了模型的构造函数之外,我们只需要实现两个函数:rowCount()返回行数,而data()返回一个对应于指定的模型索引的数据项目。

比较好的模型还会实现headerData()以为树视图和列表视图提供一些可以显示在表头上的内容。

注意这是一个非层次型的模型,所以我们不需要担心父-子对象的关系。如果我们的模型是层次型的,我们将会还需要实现index()和parent()函数。

字符串列表是存储在内部的stringList私有变量中的。

模型的维度

我们希望模型中的行数与字符串列表中字符串的个数是相同的。出于这个目的, 我们这样实现rowCount()函数:

int StringListModel ::rowCount( const QModelIndex &parent) const

{

return stringList .count();

}

由于模型是非层次型的,我们可以安全地忽略模型索引以及对应的父项目。默认情况下,从QAbstractListModel派生的模型只包含一列,所以我们不需要重新实现columnCount()函数。

模型表头和数据

对于视图中的项目,我们想要返回字符串列表中的字符串。data()函数负责返回与索引参数对应的数据项目:

QVariant StringListModel ::data( const QModelIndex &index, int role) const

{

if (!index.isValid())

return QVariant();

if (index.row() >= stringList .size())

return QVariant();

if (role == Qt::DisplayRole)

return stringList .at(index.row());

else

return QVariant();

}

我们只在以下情况才返回一个有效的QVariant:提供的模型索引是有效的,行号在字符串列表中项目数量的范围内,并且所要求的角色是我们所支持的。

某些视图,例如QTreeView和QTableView,可以在显示项目数据的同时显示表头。如果我们的模型在视图与表头一起显示,我们希望表头显示行号和列号。我们可以通过子类化headerData()函数来提供关于表头的信息:

QVariant StringListModel ::headerData( int section, Qt::Orientation orientation,

int role) const

{

if (role != Qt::DisplayRole)

return QVariant();

if (orientation == Qt::Horizontal)

return QString( "Column %1" ).arg(section);

else

return QString( "Row %1" ).arg(section);

}

同样地,我们只有角色是我们所支持的那个的时候才返回一个有效的QVariant。在决定具体地返回的数据时,表头的方向也在计算当中。

不是所有的视图都与项目数据一起显示表头,而那些会显示的可能会被配置成隐藏它们。然而,还是建议你实现headerData()函数以提供由模型所提供的数据的相关信息。

一 个项目可以拥有很多的角色,依据所指定的角色来给出不同的数据。在我们的模型中的项目只有一个角色,DisplayRole,所以我们忽略指定的角色,返 回项目的数据。然而,我们可以在其它角色中重用我们为DisplayRole提供的数据,例如ToolTipRole,视图可以用它来在一个工具提示中显 示项目的相关信息。

一个可编辑的模型

那个只读的模型展示了简单的选项如何被呈现给用户,然而,对于很多应用程序来说,一 个可编辑的列表模型是更有用的。我们可以通过以下方式来修改这个只读的模型,使得项目可被修改:改变我们为只读功能而实现的data()函数 ,并且再实现两个函数:flags()和setData()。在类的定义中加入了以下的函数声明:

Qt::ItemFlags flags( const QModelIndex &index) const ;

bool setData( const QModelIndex &index, const QVariant &value,

int role = Qt::EditRole);

使得模型可以编辑

一个代表在创建一个编辑器之前检查一个项目是否是可编辑的。模型必须让代表知道它的项目可被编辑。我们通过为每个项目返回正确的标志来实现这一点;在这种情况下,我们使所有的项目变得可用并且让它们成为可选择的和可编辑的:

Qt::ItemFlags StringListModel ::flags( const QModelIndex &index) const

{

if (!index.isValid())

return Qt::ItemIsEnabled;

return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;

}

注意,我们不需要知道代表是如何实现实际的编辑过程的。我们只需要为代表提供一个设置模型中的数据的方式。这是通过setData()函数来实现的:

bool StringListModel ::setData( const QModelIndex &index,

const QVariant &value, int role)

{

if (index.isValid() && role == Qt::EditRole) {

stringList .replace(index.row(), value.toString());

emit dataChanged(index, index);

return true ;

}

return false ;

}

在 这个模型中,字符串列表中与模型索引对应的项目被所提供的值替换。然而,在我们修改字符串列表之前,我们必须确认:索引是有效的、项目是正确的类型、角色 是被支持的。按照约定,我们坚持要求该角色是EditRole,因为这是标准的项目代表所使用的角色。然而,对于布尔值,你可以使用 Qt::CheckStateRole并且设置Qt::ItemIsUserCheckable标志;这样就会有一个选择框被用来编辑它的值。这个模型中 的底层数据对于所有角色都是相同的,因此这个细节使得将该模型与标准组件整合更加容易。

在数据被设置后,模型必须让视图知道某些数据已经改变了。这是通过发射dataChanged()信号来实现的。由于只有一个数据项目发生改变,信号中指明的项目的范围被限定为只有一个模型索引。

还有data()函数需要修改,加上Qt::EditRole测试:

QVariant StringListModel ::data( const QModelIndex &index, int role) const

{

if (!index.isValid())

return QVariant();

if (index.row() >= stringList .size())

return QVariant();

if (role == Qt::DisplayRole || role == Qt::EditRole)

return stringList .at(index.row());

else

return QVariant();

}

插入和删除行

改变一个模型中的行数和列数是有可能的。在字符串列表模型中,只有改变行数是有意义的,所以我们只要重新实现用来插入和删除行的函数。这些是在类的定义中声明的:

bool insertRows( int position, int rows, const QModelIndex &index = QModelIndex());

bool removeRows( int position, int rows, const QModelIndex &index = QModelIndex());

由于模型中的行对应着一个列表中的字符串,insertRows()函数在字符串列表中的指定位置之前插入一些空的字符串。插入的字符串的个数是与指定的行数相等的。

父索引一般是用来决定应当将行加到模型中的什么位置。在当前的情形下,我们只有一个顶级的字符串列表,所以我们直接插入空的字符串到那个列表中。

bool StringListModel ::insertRows( int position, int rows, const QModelIndex &parent)

{

beginInsertRows(QModelIndex(), position, position+rows- 1 );

for ( int row = 0 ; row < rows; ++row) {

stringList .insert(position, "" );

}

endInsertRows();

return true ;

}

模 型首先调用beginInsertRows()函数来通知其它组件:行数将要改变。这个函数指明要插入的第一行和最后一行的行号,以及它们的父对象的模型 索引。在改变了字符串列表之后,它调用endInsertRows()来完成操作,并且通知其它组件:模型的维度已经改变,返回真以指示操作成功。

从模型中删除行的函数也很容易写。要从模型中删除的行是通过位置和行的数目来指定的。我们忽略父索引以简化代码的实现,只是简单地删除字符串列表中对应的项目。

bool StringListModel ::removeRows( int position, int rows, const QModelIndex &parent)

{

beginRemoveRows(QModelIndex(), position, position+rows- 1 );

for ( int row = 0 ; row < rows; ++row) {

stringList .removeAt(position);

}

endRemoveRows();

return true ;

}

beginRemoveRows() 函数每当底层的任何数据要被删除之前都会被调用,并且指明要删除的行中的第一行和最后一行。这样允许其它组件在数据变得无效之前访问它们。在那些行被删除 后,模型发射endRemoveRows()来完成操作并且让其它组件知道模型的维度已经发生变化。

下面的步骤

我们可以使用类来以竖直列表的形式来展示由这个模型或者任意的其它模型提供的数据项目。对于这个字符串列表模型,这个视图也提供一个默认的编辑器,这样项目就可以被编辑了。我们在视图类一章来查看标准视图类所创造的可能性。

模型子类化参考文档更详细地描述了QAbstractItemModel子类的需求,并且提供了一个为了实现不同模型中各种各样的特性而必须实现的虚函数的指导。

Your opinions

Posted at:Thu Aug 12 00:22:18 2010ggarlic看得我眼疼
Posted at:Thu Aug 12 00:42:23 2010太极美术工程师师长好的。看得我眼疼
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands

 
Recent comments
2017年4月~2019年4月垃圾短信排行榜Posted at:Thu Sep 26 04:51:48 2024
Qt5.7文档翻译:QWebEngineCookieStore类,QWebEngineCookieStore ClassPosted at:Fri Aug 11 06:50:35 2023盲盒kill -9 18289 Grebe.20230517.211749.552.mp4