(三十七)模型视图设计模型

模型视图设计模式

模型视图设计模式的核心思想

1、模型(数据)与视图(显示)相分离

2、模型对外提供标准接口存取数据(不关心数据如何显示)

3、视图自定义数据的显示方式(不关系数据如何组织、存储)

 

注:模型里面并不真正存储数据(数据少的话也可以直接存储在模型里),模型只是负责从诸如磁盘文件、数据库、网络通讯等获得源数据,并提供给视图,视图对数据进行修改,然后再通过模型更新源数据。

 

模型视图模式组成

Qt中的模型视图模式分为三个部分:模型、视图、和委托(处理用户对数据项的编辑与渲染数据项,目前暂时不谈委托)

 

模型视图模式中的类

模型:

1、  抽象类QAbstractItemModel为所有模型的基类,它实现了供视图与委托的接口

2、  QAbstractItemModel提供的接口能够满足视图以表格、列表、树的形式显示数据

3、  如果要为列表或者表格设计自定义模型,建议继承自QAbstractListModel和QAbstractTableModel,这两个类已经实现了许多通用函数

内置标准模型:

   QStringListModel:存储简单的字符串列表

   QStandardItemModel:可以用于树结构的存储,提供了层次数据

   QFileSystemModel:本地系统的文件和目录信息

   QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel:存取数据库数据

 

视图

抽象类QAbstractItemView是所有视图类的基类

内置标准视图

1、 QListView把Model中的数据显示为一个列表

2、  QTableView把Model中的数据以表格的形式表现

3、  QTreeView用具有层次结构的列表来显示Model中的数据


委托

1、  抽象类QAbstractItemDelegate是委托类的基类

2、  内置有两个相互独立的标准委托QStyledItemDelegate 与QItemDelegate

3、   QStyledItemDelegate 是Qt4.4版本后的标准委托

4、   QStyledItemDelegate使用当前的样式来绘制它的项

5、  在实现自定义委托或使用Qt样式表时,使用QStyledItemDelegate作为基类。

 

 

模型视图模式直观理解


//模型视图示例

//Widget.h

 

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtGui/QWidget>
#include <QFileSystemModel>
#include <QTreeView>
 
class Widget : public QWidget
{
   Q_OBJECT
   
   QFileSystemModel m_fsModel;                         //声明系统文件模型
   QTreeView m_treeView;                            //声明树状视图
public:
   Widget(QWidget *parent = 0);
   ~Widget();
};
 
#endif // WIDGET_H


 

 

//widget.cpp

 

#include "Widget.h"
#include <QDir>                                                      /
 
Widget::Widget(QWidget *parent)
    :QWidget(parent)
{
   m_treeView.setParent(this);                                       //设置视图的父子间关系
   m_treeView.move(10, 10);                                           //设置视图类对象位置
   m_treeView.resize(500, 300);                                              //设置视图类对象大小
   m_fsModel.setRootPath(QDir::currentPath());      //设置当前路径为模型监视路径
 
   m_treeView.setModel(&m_fsModel);                      //连接视图与模型
 
         //以当前文件目录模型索引来显示模型数据,如果没有此句将会显示根目录数据
   m_treeView.setRootIndex(m_fsModel.index(QDir::currentPath()));    
}       
 
Widget::~Widget()
{
   
}


 

 

模型视图模式的工作机制(信号-槽)

当数据发生改变时,模型发出信号通知视图

当用户与视图进行交互时,视图发出信号提供交互信息

用户编辑数据项时,委托发出信号告知模型与视图编辑器的状态

数据与视图分离的重要机制:

模型索引:

在Qt中,不管模型以什么结构组织数据,都必须为每一个数据提供独一无二的索引,

模型索引提供对数据块的临时引用,视图与委托通过索引访问模型中的具体数据

 

Qt中的模型索引类QModelIndex

QModelIndex中包含了具体数据的访问途径、包含了一个指向模型的指针

QModelIndex提供的是模型的临时索引


注意:由于模型会经常重新组织其内部的数据结构,模型索引很可能失效,不应该保存模型索引。如果实在需要长期引用一个数据块,使用QPersistentModelIndex类创建永久模型索引,永久模型索引保证其引用的数据及时更新

 

 

模型中的数据以层次结构表示


List Model虽然是线性的列表,有一个 RootItem(根节点),线性的一个个数据可以看作是一个只有一列的表格,但是它是有层次的,因为有一个根节点。Table Model就比较容易理解,只是也存在一个根节点。Tree Model主要面向层次数据,而每一层次都可以都很多列,因此也是一个带有层次的表格。

 

模型中索取数据索引的通用方式

在Qt中,为了定位模型中的数据(即获取模型索引),需要三个属性:行号、列号以及父索引。即三元组:(row, column, parent)

 

当父节点为虚拟Root节点时,使用空索引(直接调用QModelIndex()产生)作为父节点


QModelIndexindexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());


QModelIndex indexA =model->index(0, 0,QModelIndex());
QModelIndex indexC =model->index(2, 1,QModelIndex());
QModelIndexindexB = model->index(1, 0,indexA);         //B为A的子项

 

//widget.h

 

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtGui/QWidget>
#include <QPlainTextEdit>
#include <QFileSystemModel>
 
class Widget : public QWidget
{
   Q_OBJECT
   
   QPlainTextEdit m_edit;
   QFileSystemModel m_fsm;
protected slots:
   void onDirectoryLoaded(const QString& path);
public:
   Widget(QWidget *parent = 0);
   ~Widget();
};
 
#endif // WIDGET_H
 


//widget.cpp

 

#include "Widget.h"
#include <QDir>
#include <QModelIndex>
#include <QByteArray>
#include <QBuffer>
#include <QTextStream>
 
Widget::Widget(QWidget *parent)
    :QWidget(parent)
{
   m_edit.setParent(this);                              //设置父子关系
   m_edit.move(10, 10);
   m_edit.resize(500, 300);
 
   connect(&m_fsm, SIGNAL(directoryLoaded(QString)), this,SLOT(onDirectoryLoaded(QString)));         //绑定信号与槽
 
   m_fsm.setRootPath(QDir::currentPath());     //设置当前路径为文件模型监视路径
}
 
void Widget::onDirectoryLoaded(constQString& path) //这里的path由m_fsm.setRootPath(QDir::currentPath());控制
{                                                                                                    
   QModelIndex root = m_fsm.index(path);       
   QByteArray array;                                                 
   QBuffer buffer(&array);                                       
 
   if( buffer.open(QIODevice::WriteOnly) )  //以只读方式打开缓冲区
    {
       QTextStream out(&buffer);                       
 
       out << m_fsm.isDir(root) << endl;  
        out << m_fsm.data(root).toString()<< endl;            //通过模型访问数据
       out << root.data().toString() << endl;                         //通过索引访问数据
        out << &m_fsm << endl;                                                //模型地址
       out << root.model() << endl;                               //获取索引内指向模型的指针
       out << m_fsm.filePath(root) << endl;
       out << m_fsm.fileName(root) << endl;
       out << endl;
 
       for(int i=0; i<m_fsm.rowCount(root); i++)      
       {
           QModelIndex ci = m_fsm.index(i, 0, root);     
 
           out << ci.data().toString() << endl;          
       }
 
       out.flush();                                                                                 
       buffer.close();                                                                             //关闭缓冲区
    }
 
   if( buffer.open(QIODevice::ReadOnly) )
    {
       QTextStream in(&buffer);
 
       m_edit.insertPlainText(in.readAll());                
 
       buffer.close();                    
    }
}
 
Widget::~Widget()
{
   
}
 


 

 

模型中的数据项的角色

数据角色的概念:

1、  模型中的数据在视图中的用途(显示方式)可能不同

2、  模型必须为数据设置特定的数据角色(数据属性)

3、  数据角色用于提示视图数据的作用

4、  数据角色是不同视图以统一风格显示数据的标准

 

Qt中的数据角色定义:

通用角色(与类型相关):

Qt::DisplayRole

以文本形式显示的关键数据(QString)

Qt::DecorationRole

以图标形式显示的数据(QColor、QIcon、QPixmap)

Qt::EditRole

适合在 编辑器中编辑的形式的数据(QString)

Qt::ToolTipRole

显示在项的ToolTip中的数据

Qt::StatusTipRole

状态栏中显示的提示性数据

Qt::WhatsThisRole

项的帮助信息数据

Qt::SizeHintRole

提供给视图的项大小数据

 

数据角色的意义:

1、  定义了数据在特定系统下的标准用途

2、  不同视图可以通过相同标准显示数据

 

注意:

1、  数据角色只是一个附加属性,这个属性代表推荐的数据显示方式

2、  不同的视图完全可以自由解析或者忽略数据的角色信息

 

 

//widget.h

 
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtGui/QWidget>
#include <QTableView>
#include <QListView>
#include <QTreeView>
#include <QStandardItemModel>
 
class Widget : public QWidget
{
   Q_OBJECT
   
/*QStandardItemModel负责保存数据,每个数据项被表示为QStandardItem对象。类QabstractItemModel,QabstractListModel,QAbstractTableModel不保存数据,需要从这些类派生出子类,并在子类中定义某种数据结构来保存数据。*/
   QStandardItemModel m_model;             
   QTableView m_tableView;                        
   QListView m_listView;                      
    QTreeViewm_treeView;
 
   void initModel();
   void initView();
public:
   Widget(QWidget *parent = 0);
   ~Widget();
};
 
#endif // WIDGET_H


 

//widget.cpp

 

#include "Widget.h"
#include <QStandardItem>
 
Widget::Widget(QWidget *parent)
    :QWidget(parent, Qt::WindowContextHelpButtonHint)
{
   initModel();
   initView();
 
   m_tableView.setModel(&m_model);      //关联视图与模型
   m_listView.setModel(&m_model);
   m_treeView.setModel(&m_model);
}
 
void Widget::initModel()
{
   QStandardItem* root = m_model.invisibleRootItem();  //获取模型的虚拟数据项(QStandardItem类对象)(虚拟的根节点)
   QStandardItem* itemA = new QStandardItem();             //构造数据项
   QStandardItem* itemB = new QStandardItem();
   QStandardItem* itemC = new QStandardItem();
   QStandardItem* itemChild = new QStandardItem();
 
   itemA->setData("A", Qt::DisplayRole);           //给数据项设置数据并为数据设置数据角色
   itemA->setData("Tip A", Qt::ToolTipRole);
   itemA->setData("Help A", Qt::WhatsThisRole);
 
   itemB->setData("B", Qt::DisplayRole);
   itemB->setData("Tip B", Qt::ToolTipRole);
 
   itemC->setData("C", Qt::DisplayRole);
   itemC->setData("Tip C", Qt::ToolTipRole);
   itemC->setData("Help C", Qt::WhatsThisRole);
 
   itemChild->setData("Child", Qt::DisplayRole);
   itemChild->setData("Tip Child", Qt::ToolTipRole);
   itemChild->setData("Help Child", Qt::WhatsThisRole);
 
   itemC->setChild(0, 0, itemChild);             //给数据项itemC设置子数据项itemChild,将子数据项设置在row为0,column为0的位置(此时
                                                                                                       //子数据项itemChild的索引三元组为(0,0,itemC)
   root->setChild(0, 0, itemA);
   root->setChild(0, 1, itemB);
   root->setChild(1, 0, itemC);
}
 
void Widget::initView()
{
   m_tableView.setParent(this);                   //设置父子关系
   m_tableView.move(10, 10);                       //位置
   m_tableView.resize(300, 100);                 //大小
 
   m_listView.setParent(this);
   m_listView.move(10, 120);
   m_listView.resize(300, 100);
 
   m_treeView.setParent(this);
    m_treeView.move(10,230);
   m_treeView.resize(300, 100);
}
 
Widget::~Widget()
{
   
}


声明:
此文根据 狄泰学院唐老师的《QT实验分析教程》创作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值