1.模型视图设计模式的核心思想
- 模型(数据)与视图(显示)相分离
- 模型对外提供标准接口存取数据(不关心数据如何显示)
- 视图自定义数据的显示方式(不关心数据如何组织存储)
2.模型视图模式的工作机制
- 当数据发生改变时,模型发出信号通知视图
- 当用户与视图进行交互时,视图发出信号提供交互信息
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFileSystemModel>
#include <QTreeView>
class Widget : public QWidget
{
Q_OBJECT
QFileSystemModel m_fsModel; //定义文件系统模型
QTreeView m_treeView; //定义树形显示视图
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
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() = default;
运行结果:

(1)模型定义标准接口(成员函数)对数据进行访问
(2)视图通过标准接口获取数据并定义显示方式
(3)模型通过信号与槽的机制通知视图数据变化
(4)模型中的数据都是以层次结果表示的
4.QModelIndex是Qt中的模型索引类
(1)包含具体数据的访问途径
(2)包含一个指向模型的指针
5.索引中的行和列
(1)线性模型可以使用行和列作为数据索引
(2)树形结构可以通过(index, parent)的方式确定节点
所以使用通用的方式------三元组(row, column, parent)
注意:当父节点为虚拟root节点时,可以使用空索引(直接调用QModelIndex()产生)作为父节点参数
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <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:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
};
#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);
//当文件系统模型加载完目录时,调用onDirectoryLoaded函数
connect(&m_fsm, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString)));
m_fsm.setRootPath(QDir::currentPath()); //设置文件系统模型的根路径为当前工作目录
}
void Widget::onDirectoryLoaded(const QString path)
{
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() = default;
运行结果:

6.不同的视图如何显示同一个模型中的数据?
数据角色的概念
- 模型中的数据在视图中的用途(显示方式)可能不同
- 模型必须为数据设置特定数据角色(数据属性)
- 数据角色用于提示视图数据的作用
- 数据角色是不同视图以统一风格显示数据的标准

Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTableView>
#include <QStandardItemModel> //通用数据模型
#include <QTreeView>
#include <QListView>
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
QStandardItemModel m_model;
QTableView m_tableView;
QListView m_listView;
QTreeView m_treeView;
void initModel();
void initView();
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
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);
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() = default;
运行结果:
