您的位置:首页 > 编程语言 > Qt开发

QT - Model/View 总结

2016-03-22 11:15 417 查看
参考 : http://blog.chinaunix.net/uid-20940095-id-66122.html
models,views,delegates之间通过信号,槽机制来进行通讯:

从model发出的信号通知view数据源中的数据发生了改变。

从view发出的信号提供了有关被显示的数据项与用户交互的信息。

从delegate发生的信号被用于在编辑时通知model和view关于当前编辑器的状态信息.

Views

不同的view都完整实现了各自的功能:

QListView把数据显示为一个列表,

QTableView把Model 中的数据以table的形式表现,

QTreeView
用具有层次结构的列表来显示model中的数据。

这些类都基于QAbstractItemView抽象基类,尽管这些类都是现成的,完整的进行了实现,但它们都可以用于子类化以便满足定制需求。

QItemSelectionModel
View中数据项选择机制由QItemSelectionModel类提供。所有标准的view缺省都构建它们自己的选择模型,

View被选择的数据项的信息保持在QItemSelectionModel类,在进行选择操作时,可以把QItemSelectionModel看成是model中所有数据项选择状态的一个记录。一旦建立一个选择模型,所有项的集合都可以选择,撤消选择,或者选择状态进行切换而不需要知道哪个数据项是否已经被选择过。所有被选择的项的indexes在任何时候都可以得到,通过信号槽机制可以通知别的组件发生的变化。

=================================

Models

所有的item models都基于QAbstractItemModel类,这个类定义了用于views和delegates访问数据的接口。

数据本身不必存储在model,数据可被置于一个数据结构或另外的类,文件,数据库,或别的程序组件中。

关于model的基本概念在Model Classes部分中描述。

QAbstractItemModel提供给数据一个接口,它非常灵活,基本满足views的需要,无论数据用以下任何样的形式

表现,如tables,lists,trees。然而,当你重新实现一个model时,如果它基于table或list形式的数据结构,最好从QAbstractListModel,QAbstractTableModel开始做起,因为它们提供了适当的常规功能的缺省实现。这些类可以被子类化以支持特殊的定制需求。子类化model的过程在Create New Model部分讨论

QT提供了一些现成的models用于处理数据项:

QStringListModel 用于存储简单的QString列表。

QStandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。

QDirModel 提供本地文件系统中的文件与目录信息。

QSqlQueryModel, QSqlTableModel,QSqlRelationTableModel用来访问数据库。

假如这些标准Model不满足你的需要,你应该子类化QAbstractItemModel,QAbstractListModel或是QAbstractTableModel来定制

在model/view架构中,为model提供了统一的访问数据的方式,即Model Index。

列表model,通过QModelIndex::row()即可访问;

表格model,需要QModelIndex::row()和QModelIndex::column();

树model,除了上述两个函数,还需要提供QModelIndex::parent()来对父节点进行访问。

Model Indexes

为了使数据存储与数据访问分开,引入了model index的概念。通过model index,可以引用model中的数据项,Views和delegates都使用indexes来访问数据项,然后再显示出来。

临时的model indexes由QModelIndex提供,而具有持久能力的model indexes则由QPersistentModelIndex提供。在获取对应一个数据项的model index时,需要考虑有关于model的三个属性:行数,列数,父项的model index。

eg: QModelIndex indexA = model->index(0, 0,QModelIndex());

一个model的顶级项,由QModelIndex()取得,它们上式被用作父项。

父项

QModelIndex indexA = model->index(0, 0, QModelIndex());

QModelIndex indexB = model->index(1, 0, indexA);

项角色

通过指定model index与角色来获取我们需要的数据:

QVariant value = model->data(index, role);

概念总结:

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.
}

QSortFilterProxyModel
不能单独使用,它只是一个“代理”,真正的数据需要另外的一个model提供,而且它是用来排序和过滤的。

设计一个Model

一个只读model示例

当我们实现一个model时,不要忘了QAbstractItemModel本身不存储任何数据,它仅仅提供了给views访问数据的接口。
class StringListModel : public QAbstractListModel
{
Q_OBJECT

public:
StringListModel(const QStringList &strings, QObject *parent = 0)
: QAbstractListModel(parent), stringList(strings) {}

introwCount(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

我们只需要给只读的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;

returnQAbstractItemModel::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());

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;
}


便利类

许多便利类都源于标准的view类,它们方便了那些使用Qt中基于项的view与table类,它们不应该被子类化,它们只是为Qt 3的等价类提供一个熟悉的接口。

这些类有QListWidget,QTreeWidget,QTableWidget,它们提供了如Qt 3中的QListBox, QlistView,QTable相似的行为。

这些类比View类缺少灵活性,不能用于任意的models,推介使用model/view的方法处理数据。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: