QT中的Model/View框架实现了标准MVC架构的功能,但是他的体系架构和标准MVC有些差别。
模型类:抽象基类,定义了模型访问接口。负责访问数据集中的数据项
委托类:抽象基类,负责视图中每个数据项的绘制与编辑
视图类:抽象基类,负责绘制总体外观并处理用户的交互命令
选择:QItemSelectionRange对象,表示多个选择块的信息
模型:依据数据项之间的关系,具有三种拓扑图
不可见根的作用:三种拓扑图都可以看作树模型
数据子项(模型中的一个数据项会存放多个数据子项)和角色(一个数据子项所起的作用)
一个数据项可以被看作多个【角色,数据子项】对组成的集合
(目标节点的)索引:QModelIndex对象,给定一个索引,不再需要任何其他信息,就可以唯一确定一个模型对象中的一个数据项
派生新的模型类实现最小模型访问接口
- 选择合适的基类:
- QAbstractItemModel
- 基于上面的选择进行不同操作
- 任何派生类都必须实现这个五个函数,这五个函数是模型类和视图类之间的最小接口
满二叉树例子:
概念:指除了最底层的节点(被称为叶子节点)之外,每个高层节点都具有2个子节点。二叉树的每个节点存放着一个整数,每个父节点中的数是它的两个子节中数的和
TreeModel类的声明
#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel();
int rowCount ( const QModelIndex & parent ) const;
int columnCount ( const QModelIndex & parent ) const;
QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const;
QModelIndex parent ( const QModelIndex & index ) const;
QVariant data ( const QModelIndex & index, int role ) const;
private:
enum {N=15};
int numbers[N];
};
#endif // TREEMODEL_H
作用:将上图的满二叉树存放在成员数组numbers中
TreeModel::TreeModel()
{
int values[N]={36,10,26,3,7, 11,15, 1,2,3,4,5,6,7,8};
for (int i=0; i<N; i++)
numbers[i] = values[i];
}
虚函数作用:返回一个父节点所含子节点的个数
0:叶子节点
1:根节点
2:非叶节点
int TreeModel::rowCount ( const QModelIndex & parent ) const
{
if ( ! parent.isValid() )
return 1;
if (parent.internalId() < N/2 )
return 2;
return 0;
}
int TreeModel::columnCount ( const QModelIndex & parent ) const
{
return 1;
}
虚函数作用:返回一个数据项的索引
QModelIndex TreeModel::index ( int row/*子数据项所在的行号*/, int column/*子数据项所在列号*/, const QModelIndex & parent/*该数据项父节点的索引*/ ) const
{
if ( ! parent.isValid() ){
quintptr id = 0;
return createIndex(row, column, id);
}
int parent_idx = parent.internalId();
int idx = parent_idx * 2 + ( row + 1 );//公式
return createIndex(row, column, idx );
}
虚函数parent()的作用:返回一个子数据项父节点的索引
QModelIndex TreeModel::parent ( const QModelIndex & index/*子数据项的索引*/ ) const
{
if (index.internalId() == 0)
return QModelIndex();//无效索引:不可见根
int parent_idx = (index.internalId() - 1 )/2; //公式
return createIndex( (parent_idx+1) % 2, 0, parent_idx );
}
虚函数data()的作用:获取一个数据项中的数据
QVariant TreeModel::data ( const QModelIndex & index/*该数据项的索引*/, int role/*该数据项所起的作用*/ ) const
{
switch (role) {
case Qt::DisplayRole:
int value = numbers[ index.internalId() ];
return QVariant( value );
}
return QVariant();
}
主函数作用: 建立模型类和视图类之间的关系【通过调用视图类的setModel(...)】
展示自己【通过调用视图类的show()】
#include "treemodel.h"
#include <QApplication>
#include <QTreeView>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
TreeModel treeModel;
QTreeView treeView(0);
treeView.setModel( & treeModel );
treeView.show();
return app.exec();
}
对应的设计流程图
效果图
处理更多的角色:修改data()函数
数据变化时通知视图类:数据发生改变→模型类发出dataChanged信号→视图类中的槽函数就会被调用
TreeModel::TreeModel()
{
...
timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()) , this, SLOT(timerHit()));
timer->start();
}
//槽函数
void TreeModel::timerHit()
{
numbers[14] = ( numbers[14] + 1 ) % 60;
numbers[6] = numbers[14]+ numbers[13];
numbers[2] = numbers[6] + numbers[5];
numbers[0] = numbers[2] + numbers[1];
QModelIndex idx_14 = createIndex(1,0,14);
QModelIndex idx_6 = createIndex(1,0,6);
QModelIndex idx_2 = createIndex(1,0,2);
quintptr id = 0;
QModelIndex idx_0 = createIndex(0,0,id);
emit dataChanged(idx_14, idx_14);
emit dataChanged(idx_6, idx_6 );
emit dataChanged(idx_2, idx_2 );
emit dataChanged(idx_0, idx_0 );
}
编辑数据项:模型类负责确定哪些是可编辑的,哪些是不可编辑的→用户双击→视图类会调用模型类的成员函数flags()→可以编辑→视图类调用模型类的data()函数→送给一个编辑器控件进行编辑→QT的M/V框架会调用模型类的setData()函数:编辑器的编辑结果👉模型类→模型类修改目标数据项中的数据→触发dataChanged()信号→绑定的视图对象→重新绘制目标数据项(1.委托类 2.多个视图对象绑定一个模型类对象)
Qt::ItemFlags TreeModel::flags(const QModelIndex & index) const
{
if ( index.internalId() < N/2 )
return Qt::ItemIsSelectable | Qt::ItemIsEnabled ;
else
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
QVariant TreeModel::data ( const QModelIndex & index, int role ) const
{
int value;
switch (role) {
case Qt::DisplayRole:
value = numbers[ index.internalId() ];
return QVariant( value );
case Qt::EditRole:
value = numbers[ index.internalId() ];
return QVariant( value );
}
return QVariant();
}
bool TreeModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if (role != Qt::EditRole) return true;
int id = index.internalId();
while (1) {
if ( id >= N/2 )
numbers[id] = value.toInt();
else
numbers[id] = numbers[2 * id + 1] + numbers[ 2 * id + 2];
QModelIndex idx = createIndex(1,0,id);
emit dataChanged(idx,idx);
if ( id == 0 ) break;
id = ( id - 1 ) /2;
};
return true;
}