Qt Model/View 学习笔记(二)

Model类

基本概念
在model/view构架中,model为view和delegates使用数据提供了标准接口。在Qt中,标准接口QAbstractItemModel类中被定义。不管数据在底层以何种数据结构存储,QAabstractItemModel的子类会以层次结构的形式来表示数据,结构中包含了数据项表。我们按这种约定来访问model中的数据项,但这个约定不会对如何显示这些数据有任何限制。数据发生改变时,model通过信号槽机制来通知关联的views。
Model Indexes
为了使数据存储与数据访问分开,引入了model index的概念。通过model index,可以引用model中的数据项,Views和delegates都使用indexes来访问数据项,然后再显示出来。因此,只有model需要了解如何获取数据,被model管理的数据类型可以非常广泛地被定义。Model indexes包含一个指向创建它们的model的指针,这会在配合多个model工作时避免混乱。
QAbstractItemModel  *model = index. model();
model indexes提供了对一项数据信息的临时引用,通过它可以访问或是修改model中的数据。既然model有时会重新组织内部的数据结构,这时model indexes便会失效,因此不应该保存临时的model indexes。假如需要一个对数据信息的长期的引用,那么应该创建一个 persistent  model index。这个引用会保持更新。临时的model indexes由 QModelIndex提供,而具有持久能力的model indexes则由 QPersistentModelIndex提供。在获取对应一个数据项的model index时,需要考虑有关于model的三个属性:行数,列数,父项的model index。

行与列
在最基本的形式中,一个model可作为一个简单的表来访问,每个数据项由行,列数来定位。这必不意味着
底层的数据用数组结构来存储。行和列的使用仅仅是一种约定,它允许组件之间相互通讯。可以通过指定
model中的行列数来获取任一项数据,可以得到与数据项一一对应的那个index。
QModelIndex  index = model-> index(row, column, ...);
Model为简单的,单级的数据结构如list与tables提供了接口,它们如上面代码所显示的那样,不再需要别的信息被提供。当我们在获取一个model index时,我们需要提供另外的信息。
上图代表一个基本的table model,它的每一项用一对行列数来定位。通过行列数,可以获取代表一个数据项的model index .  
QModelIndex  indexA = model-> index(0, 0,   QModelIndex());
  QModelIndex  indexB = model-> index(1, 1,   QModelIndex());
  QModelIndex  indexC = model-> index(2, 1,   QModelIndex());
一个model的顶级项,由QModelIndex()取得,它们上式被用作父项。
父项
类似于表的接口在搭配使用table或list view时理想的,这种行列系统与view显示的方式是确切匹配的。
然则,像tree views这种结构需要model提供更为灵活的接口来访问数据项。每个数据项可能是别的项的
父项,上级的项可以获取下级项的列表。
当获取model中数据项的index时,我们必须指定关于数据项的父项的信息。在model外部,引用一个数据
项的唯一方法就是通过model index,因此需要在求取model index时指定父项的信息。
QModelIndex  index = model-> index(row, column, parent);

上图中,A项和C项作为model中顶层的兄弟项:
  QModelIndex  indexA = model-> index(0, 0, QModelIndex());
  QModelIndex  indexC = model-> index(2, 1, QModelIndex());
A有许多孩子,它的一个孩子B用以下代码获取:
QModelIndex  indexB = model-> index(1, 0, indexA);

项角色
model中的项可以作为各种角色来使用,这允许为不同的环境提供不同的数据。举例来说,Qt::DisplayRole被用于访问一个字符串,它作为文本会在view中显示。典型地,每个数据项都可以为许多不同的角色提供数据,标准的角色在Qt::ItemDataRole中定义。我们可以通过指定model index与角色来获取我们需要的数据:
QVariant  value = model-> data(index, role);
角色指出了从model中引用哪种类型的数据。views可以用不同的形式显示角色,因此为每个角色提供正确
的信息是非常重要的。通过为每个角色提供适当数据,model也为views和delegates提供了暗示,如何正确地
把这些数据项显给用户。不同的views可以自由地解析或忽略这些数据信息,对于特殊的场合,也可以定义
一些附加的角色。
概念总结:
1,Model indexes为views与delegages提供model中数据项定位的信息,它与底层的数据结构无关。
2,通过指定行,列数,父项的model index来引用数据项。
3,依照别的组件的要求,model indexes被model构建。
4,使用index()时,如果指定了有效的父项的model index,那么返回得到的model index对应于父项的某个孩子。
5,使用index()时,如果指定了无效的父项的model index,那么返回得到的model index对应于顶层项的某个孩子。
6, 角色对一个数据项包含的不同类型的数据给出了区分。
使用Model Indexes
QDirModel  *model = new   QDirModel;
          QModelIndex  parentIndex = model-> index(QDir::currentPath());
         int numRows = model-> rowCount(parentIndex);
  for (int row = 0; row < numRows; ++row)
  {
                  QModelIndex  index = model-> index(row, 0, parentIndex);
                 tring text = model-> data(index, Qt::DisplayRole).toString();
               // Display the text in a widget.
         }
以上的例子说明了从model中获取数据的基本原则:
1,model的尺寸可以从 rowCount()与 columnCount()中得出。这些函数通常都需要一个表示父项的model index。
2,model indexes用来从model中访问数据项,数据项用行,列,父项model index定位。
3, 为了访问model顶层项,可以使用 QModelIndex()指定。
4, 数据项为不同的角色提供不同的数据。为了获取数据,除了model index之外,还要指定角色。
 
创建新的Models

介绍
model/view组件之间功能的分离,允许创建model利用现成的views。这也可以使用标准的功能图形用户接口组件像QListView,QTableView和QTreeView来显示来自各种数据源的数据为。
QAbstractListModel类提供了非常灵活的接口,允许数据源以层次结构的形式来管理信息,也允许以某种
方式对数据进行插入、删除、修改和存储。它也提供了对拖拽操作的支持。
QAbstractListModelQAbstractTableModel为简单的非层次结构的数据提供了接口,对于比较简单的list和table models来说,这是不错的一个开始点。

设计一个Model
当我们为存在的数据结构新建一个model时,首先要考虑的问题是应该选用哪种model来为这些数据提供接口。
假如数据结构可以用数据项的列表或表来表示,那么可以考虑子类化 QAbstractListModelQAbstractTableModel
,既然这些类已经合理地对许多功能提供缺省实现。
然而,假如底层的数据结构只能表示成具有层次结构的树型结构,那么必须得子类化 QAbstractItemModel
无论底层的数据结构采取何种形式,在特定的model中实现标准的 QAbstractItemModel  API总是一个不错的主意,这使得可以使用更自然的方式对底层的数据结构进行访问。这也使得用数据构建model 更为容易,其他
的model/view组件也可以使用标准的API与之进行交互。

一个只读model示例
这个示例实现了一个简单的,非层次结构的,只读的数据model,它基于 QStringistModel类。它有一个 QStringList作为它内部的数据源,只实现了一些必要的接口。为了简单化,它子类化了 QAbstractListModel,这个基类提供了合理的缺省行为,对外提供了比 QAbstractItemModel更为简单的接口。当我们实现一个model时,不要忘了 QAbstractItemModel本身不存储任何数据,它仅仅提供了给views访问
数据的接口。
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::Orientation orientation,
                                                 int role = Qt::DisplayRole) const;
  private:
          QStringList  stringList;
  };
除了构造函数,我们仅需要实现两个函数: rowCount()返回model中的行数, data()返回与特定model index对应的数据项。具有良好行为的model也会实现 headerData(),它返回tree和table views需要的,在标题中显示的数据。
因为这是一个非层次结构的model,我们不必考虑父子关系。假如model具有层次结构,我们也应该实现 index()与 parent()函数。

Model的尺寸
我们认为model中的行数与string list中的string数目一致:
int   StringListModel:: rowCount(const QModelIndex &parent) const
  {
         return stringList.count();
  }
在缺省情况下,从 QAbstractListModel派生的model只具有一列,因此不需要实现columnCount()。

Model 标题与数据
  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   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);
  }
一个数据项可能有多个角色,根据角色的不同输出不同的数据。上例中,model中的数据项只有一个角色 ,
DisplayRole,然而我们也可以重用提供给DisplayRole的数据,作为别的角色使用,如我们可以作为ToolTipRole来用。

可编辑的model
上面我们演示了一个只读的model,它只用于向用户显示,对于许多程序来说,可编辑的list model可能更有用。我们只需要给只读的model提供另外两个函数flags()与setData()的实现。下列函数声明被添加到类定义中:
         Qt:: ItemFlags   flags(const QModelIndex &index) const;
         bool   setData(const QModelIndex &index, const QVariant &value,
                                   int role = Qt::EditRole);

让model可编辑
delegate会在创建编辑器之前检查数据项是否是可编辑的。model必须得让delegate知道它的数据项是可
编辑的。这可以通过为每一个数据项返回一个正确的标记得到,在本例中,我们假设所有的数据项都是
可编辑可选择的:
Qt:: ItemFlags   StringListModel:: flags(const QModelIndex &index) const
  {
         if (!index.isValid())
                 return Qt::ItemIsEnabled;
         return   QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
  }
我们不必知道delegate执行怎样实际的编辑处理过程,我们只需提供给delegate一个方法,delegate会使用它对model中的数据进行设置。这个特殊的函数就是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;
  }
当数据被设置后,model必须得让views知道一些数据发生了变化,这可通过发射一个dataChanged() 信号实现。
因为只有一个数据项发生了变化,因此在信号中说明的变化范围只限于一个model index。
插入,删除行
在model中改变行数与列数是可能的。当然在本列中,只考虑行的情况,我们只需要重新实现插入、删除
的函数就可以了,下面应在类定义中声明:
         bool   insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
         bool   removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
既然model中的每行对应于列表中的一个string,因此,insertRows()函数在string list   中指定位置插入一个空string,
父index通常用于决定model中行列的位置,本例中只有一个单独的顶级项,困此只需要在list中插入空string。
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()对操作进行确认与通知。
返回true表示成功。
删除操作与插入操作类似:
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;
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值