做GUI应用程序开发的同学想必都知道MVC设计模式,MVC即Model-View-Controller,模型-视图-控制器。现我试图将Qt中的“MVC”讲清楚,先从简单的模型-视图说起。
1. 模型-视图基本概念
模型-视图中,模型用于数据的存放/管理,视图用于数据的显示。模型-视图的核心思想是将模型和视图解耦,即将二者分离:模型对外提供标准接口,通过接口外界可以存取数据,模型不需要关心数据的显示;视图负责定义数据的显示方式,它不需要知道数据的组织存储。模型-视图从概念图上可以直观的理解为:
Qt中提供了几个现成的模型用于处理数据项,当然了,所谓模型其实就是c++中的类。
(1) QStringListModel:用于存储简单的QString类型数据的列表项
(2) QStandardItemModel:用于管理组成树形结构存储的数据项,其中每一个数据项可以是任意类型的数据
(3) QFileSystemModel:用于管理有关本地文件系统的文件和目录信息
这些模型类都继承自QAbstractTableModel:
同理,Qt也提供了几个现成的视图用于数据显示:
(1) QListView:列表形式显示模型中的数据项
(2) QTableView:表格形式显示模型中的数据项
(3) QTreeView:树形结构显示模型中的数据项
这些类都继承自QAbstractItemView:
2. 模型-视图的工作机制
模型-视图的工作,主要依赖于信号与槽机制,
(1) 当数据发生改变时,模型发出信号以通知视图
(2) 当用户和视图进行交互时,视图发出信号通知模型以提供交互信息
3. 模型定义的索引
问:视图如何找到模型中的数据?
前面讲到模型“对外提供标准接口,通过接口外界可以存取”,这个标准的接口就是索引,视图通过模型提供的索引访问模型中的具体数据,也就是说Qt模型中必须为模型中的每一个数据项提供一个独一无二的索引,这是模型-视图中的关键技术。这个索引接口定义在QAbstractItemModel类中,它是一个虚函数:
virtual QModelIndex index(int row,int column, const QModelIndex &parent = QModelIndex()) const = 0;
该函数的返回的是一个QModelIndex的对象,QModelIndex是Qt模型中的索引类,它其中包含了具体数据访问的途径和一个指向模型的指针:
QVariant data(int role = Qt::DisplayRole) const; //获取该索引指向的数据,返回值类型QVariant这个后面说
const QAbstractItemModel* model() const; //获取该索引所在的模型
该函数的返回值不难理解,难以理解的是3个形参。这个得从Qt模型的索引机制讲起。
Qt中要获取模型中的某个数据项,需要知道3个量:行号、列号、父节点,这3量合称为三元组。索引在需要时由模型实时创建。
视图的分类主要有:行(List)、列表(Table)和树形(Tree)。对应到模型中的数据项的摆放也主要是这3种结构。下面分别对这3种数据组织模型的存取举例。需要说明,对数据的存取不单单是下面所举例的方法,详细内容可参阅Qt Create提供的帮助文档。
(1) 行结构数据项模型
行结构的数据项存放于QStringListModel模型中,
//Widget.h
class Widget : public QWidget
{
Q_OBJECT
public:
QStringListModel m_model; //列表模型
QListView m_listview; //列表视图
void initListMode(); //列表模型初始化函数
void initListView(); //列表视图初始化函数
QPushButton testBtn; //按钮,用于测试索引数据
public:
Widget(QWidget *parent = 0);
~Widget();
public slots:
void onTestBtnClick(); //按钮槽函数
};
//Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent), testBtn(this)
{
//初始化按钮并连接信号与槽
testBtn.move(200, 140);
testBtn.setText("inedx");
connect(&testBtn, SIGNAL(clicked()), this, SLOT(onTestBtnClick()));
//初始化模型和视图
initListMode();
initListView();
//将视图和模型挂接
m_listview.setModel(&m_model);
}
void Widget::initListMode()
{
m_model.setParent(this);
QStringList data;
data << "Hello" << "World" << "Qt";
m_model.setStringList(data);
}
void Widget::initListView()
{
m_listview.setParent(this);
m_listview.move(