模型试图设计模式的核心思想:
1、模型(数据)与视图(显示)相分离;
2、模型对外提供标准接口存取数据(不关系数据如何显示);
3、视图自定义数据的显示方式(不关系数据如何被组织和存储)。
直观理解:
其中Model指的就是 组织数据的方式。
工作机制:
一、当数据发生改变时,模型发生信号通知视图,视图对数据的变化做出相应的调整。(自由解析)
二、当用户与视图进行交互时,视图发出信号,通过模型提取相应的数据。
Qt中的模型层次结构:
Qt中的视图层次结构:
问题1:模型如何为数据提供统一的访问方式?
QFileSystemModel m_fsModel; //定义模型:文件系统
QTreeView m_treeView;//定义视图:树形
m_treeView.setParent(this);
m_treeView.move(10, 10);
m_treeView.resize(500, 300);
m_fsModel.setRootPath(QDir::currentPath()); //从当前工作目录中取数据:地址
m_treeView.setModel(&m_fsModel);//连接模型与视图
//设置为树形视图的数据索引,并从根部开始显示path中的内容
m_treeView.setRootIndex(m_fsModel.index(QDir::currentPath()));
敲黑板:
在Qt中,不管模型以什么结构组织数据,都必须为每一个数据提供独一无二的索引,而视图就通过索引来对数据进行访问。
模型视图设计模式要点:
模型通过定义标准接口(成员函数)来对数据进行访问;
视图通过标准接口获取数据并定义显示方式;
模型使用信号与槽机制通知视图数据变化;
模型中的数据都是以层次机构表示的。
模型中的索引:
模型索引是数据与视图分离的重要机制;
模型中的数据使用唯一的索引来访问;
QModelIndex是Qt中的模型索引类,包括:
1、具体数据的访问途径;
2、一个指向模型的指针。
索引的意义:
注:特殊的模型可以自定义特殊的索引方式
索引的表示方法:
1、行和列:(row, column) 缺点:对树形结构的数据无法适用
2、通用树结构:
3、三元组:
注意:当父结点为虚拟root节点时,可以使用空索引(直接调用QModelIndex()产生)作为父节点参数。
.h文件如下:
#include <QWidget>
#include <QPlainTextEdit>
#include <QFileSystemModel>
class Widget : public QWidget
{
Q_OBJECT
QPlainTextEdit m_edit;
QFileSystemModel m_fsModel;
protected slots:
void onDirectoryLoaded(const QString& path);
public:
Widget(QWidget *parent = 0);
~Widget();
};
实现文件如下:
#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_fsModel, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString)));
m_fsModel.setRootPath(QDir::currentPath());
}
void Widget::onDirectoryLoaded(const QString& path)
{
QModelIndex root = m_fsModel.index(path);
QByteArray array;
QBuffer buffer(&array);
if( buffer.open(QIODevice::WriteOnly) )
{
QTextStream out(&buffer);
out << m_fsModel.isDir(root) << endl;
out << m_fsModel.data(root).toString() << endl;
out << root.data().toString() << endl;
out << &m_fsModel << endl;
out << root.model() << endl;
out << m_fsModel.filePath(root) << endl;
out << m_fsModel.fileName(root) << endl;
out << endl;
for(int i=0; i<m_fsModel.rowCount(root); i++)
{
QModelIndex ci = m_fsModel.index(i, 0, root);
QModelIndex cj = m_fsModel.index(i, 1, root);
QModelIndex ck = m_fsModel.index(i, 2, root);
QModelIndex cl = m_fsModel.index(i, 3, root);
out << ci.data().toString() << " "<< cj.data().toString() << " "<< ck.data().toString() << " "<< cl.data().toString() <<endl;
}
out.flush();
buffer.close();
}
if( buffer.open(QIODevice::ReadOnly) )
{
QTextStream in(&buffer);
m_edit.insertPlainText(in.readAll());
buffer.close();
}
}
Widget::~Widget()
{
}
实现效果:
问题2:不同的视图如何显示同一个模型中的数据?有何特点?
问题效果如下:
如果按照上述方式进行数据组织,那么会导致一部分数据无法正常在视图中显示出来。
引入数据角色的概念:
1、模型中的数据在视图中的用途(显示方式)可能是不同的;
2、模型必须为数据设置特定的数据角色(数据属性);
3、数据角色用于确定不同数据在视图中的显示方式;
4、数据角色广义上指的是不同视图以同一风格显示数据的标准。
Qt中的数据角色定义:
数据角色的意义:
1、定义了数据在特定系统下的标准用途;
2、不同的视图可以以相同标准显示数据。
注:数据角色只是一个附加的属性,代表推荐的数据显示方式。
划重点:::
不同视图完全可以自由解析或者忽略数据的角色信息。
编程表示:
.h文件如下:
#include <QWidget>
#include <QTableView>
#include <QStandardItemModel>
#include <QListView>
#include <QTreeView>
class Widget : public QWidget
{
Q_OBJECT
QStandardItemModel m_model;
QTableView m_tableView;
QTreeView m_treeView;
QListView m_listView;
void initModel();
void initView();
public:
Widget(QWidget *parent = 0);
~Widget();
};
实现文件:
#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* 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);
//itemB->setData("Help B", Qt::WhatsThisRole);
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);
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()
{
}
实现效果:
很清楚的看到,作为itemC的子类对象并没有在tableView和listView中显示出来。
所以在组织数据的时候,要根据数据特点,选择合适的视图来对数据进行表示。