QT的Model/View框架系列之工作原理

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值