MVC设计模式是起源于Smalltalk的一种与用户界面相关的设计模式。通过使用此模型,可以有效地分离数据和用户界面。
MVC设计模式包括三个元素,表示数据的模型(Model)、表示用户界面的视图(View) 和定义了用户在界面上的操作控制(Controller)。
与MVC设计模式类似,Qt引入了模型/视图结构用于完成数据与界面的分离,即InterView框架。
*但不同的是,Qt的InterView框架中把视图和控制部件结合在一起,使得框架更为简洁。为了灵活地处理用户输入,InterView框架引入了代理(delegate)。通过使用代理,能够自定义数据条目(item)的显示和编辑方式。
其中,模型与数据源通信,并为其他部件提供接口;而视图从模型中获得用来引用数据条目的模型索引(Model Index)。
在视图中,代理负责绘制数据条目,当编辑条目时,代理和模型直接进行通信。
●数据发生改变时,模型发出信号通知视图。
●用户对界面进行操作,视图发出信号。
●代理发出信号告知模型和视图编辑器目前的状态。
InterView框架中的所有模型都基于抽象基类QAbstractItemModel类,此类由QProxyModel、QAbstractListModel、
QAbstractTableModel、 QAbstractProxyModel、QDirModel、QFileSystemModel、QHelpContentModel和
QStandardItemModel类继承。
其中,QAbstractListModel 类和QAbstractTableModel 类是列表和表格模型的抽象基类,如果需要实现列表或表格模
型,则应从这两个类继承。完成QStringList存储的QStringListModel继承自QAbstractListModel类,而与数据库有关的
QSqlQueryModel类继承自QAbstractTableModel 类; QAbstractProxyModel 类是代理模型的抽象类;QDirModel类是文件
和目录的存储模型。
InterView框架中的所有视图都基于抽象基类QAbstractItemView 类,此类由QColumnView、QHeaderView、QListView、
QTableView 和QTreeView类继承。
其中,QListView 类由QUndoView 类和QListWidget 类继承;QTableView 类由QTableWidget类继承; QTreeView 类由
QTreeWidget类继承。而QListWidget类、QTableWidget类和QTreeWidget类实际上已经包含了数据,是模型/视图集成
在一起的类。
InterView框架中的所有代理都基于抽象基类QAbstractItemDelegate 类,此类由QItemDelegate和QStyledItemDelegate
类继承。其中,QItemDelegate 类由表示数据库中关系代理的QSqlRelationalDelegate类继承。
介绍如何使用Qt中提供的QDirModel类、QTreeView 类、QListView 类和QTableView类。完成效果如下图所示。
(1) 新建Qt Gui应用 ,选择Qt Widgets Application,项目名称为“DtxModel”, 基类选择“ QMainWindow”,类名命
名为“DtxWindows”,勾选“创建界面”复选框的选中状态。 如下图所示。
(2)、创建完成工程“DtxModel”后,在其源文件"main.cpp"中加入如下代码:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//创建QDirModel模型对象,访问本地文件系统提供数据模型
QDirModel model;
//新建3种不同的view视图对象,便于文件目录可以以三种不同方式显示
//创建QTreeView类对象
QTreeView tree;
//创建QListView类对象
QListView list;
//创建QTableView类对象
QTableView table;
//在model对象中设立view对象
tree.setModel(&model);
list.setModel(&model);
table.setModel(&model);
//多选
tree.setSelectionMode(QAbstractItemView::MultiSelection);
list.setSelectionModel(tree.selectionModel());
table.setSelectionModel(tree.selectionModel());
QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),&list,SLOT(setRootIndex(QModelIndex)));
QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),&table,SLOT(setRootIndex(QModelIndex)));
QSplitter *splitter = new QSplitter;
splitter->addWidget(&tree);
splitter->addWidget(&list);
splitter->addWidget(&table);
splitter->setWindowTitle(QObject::tr("Model/View"));
splitter->show();
return a.exec();
}
(3)、在“main.cpp” 文件的开始部分加入以下头文件:
#include
#include
#include
#include
#include
#include
#include
#include
实现自定义模型可以通过QAbstractItemModel 类继承, 也可以通过QAbstractListModel和QAbstractTableModel类继承实现列表模型或表格模型。在数据库中通常需要首先将一些重复的文字字段使用数值代码保存,然后通过外键关联操作来查找其真实的含义,这一方法是为了避免冗余。
通过实现将数值代码转换为文字的模型来介绍如何使用自定义模型。此模型中保存了不同军种的各种武器。
(1) 新建Qt Gui应用 ,选择Qt Widgets Application,项目名称为“DtxModelEx”, 基类选择“ QMainWindow”,类名命
名为“mainwindow”,勾选“创建界面”复选框的选中状态。 如下图所示。
(2)、新建modelex.h头文件和modelex.cpp源文件, ModelEx 类继承自QAbstractTableModel 类,头文件“modelex.h”中
的具体代码如下:
#ifndef MODELEX_H
#define MODELEX_H
#include
#include
#include
#include
class ModelEx : public QAbstractTableModel
{
Q_OBJECT
public:
explicit ModelEx(QObject *parent = 0);
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
signals:
public slots:
private:
QVector<short> army;
QVector<short> weaponType;
//数值、文字的映射
QMap<short,QString> armyMap;
QMap<short,QString> weaponTypeMap;
QStringList weapon;
QStringList header;
//表格数据的初始化填充
void populateModel();
};
#endif // MODELEX_H
源文件"modelex.cpp"中的具体代码如下:
#include "modelex.h"
ModelEx::ModelEx(QObject *parent) :
QAbstractTableModel(parent)
{
armyMap[1]=tr("空军");
armyMap[2]=tr("海军");
armyMap[3]=tr("陆军");
armyMap[4]=tr("海军陆战队");
weaponTypeMap[1]=tr("轰炸机");
weaponTypeMap[2]=tr("战斗机");
weaponTypeMap[3]=tr("航空母舰");
weaponTypeMap[4]=tr("驱逐舰");
weaponTypeMap[5]=tr("直升机");
weaponTypeMap[6]=tr("坦克");
weaponTypeMap[7]=tr("两栖攻击舰");
weaponTypeMap[8]=tr("两栖战车");
populateModel();
}
void ModelEx::populateModel()
{
header<<tr("军种")<<tr("种类")<<tr("武器");
army<<1<<2<<3<<4<<2<<4<<3<<1;
weaponType<<1<<3<<5<<7<<4<<8<<6<<2;
weapon<<tr("B-2")<<tr("尼米兹级")<<tr("阿帕奇")<<tr("黄蜂级")<<tr("阿利伯克级")<<tr("AAAV")<<tr("M1A1")<<tr("F-22");
}
int ModelEx::columnCount(const QModelIndex &parent) const
{
return 3;
}
int ModelEx::rowCount(const QModelIndex &parent) const
{
return army.size();
}
QVariant ModelEx::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role==Qt::DisplayRole)
{
switch(index.column())
{
case 0:
return armyMap[army[index.row()]];
break;
case 1:
return weaponTypeMap[weaponType[index.row()]];
break;
case 2:
return weapon[index.row()];
default:
return QVariant();
}
}
return QVariant();
}
QVariant ModelEx::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
return header[section];
return QAbstractTableModel::headerData(section,orientation,role);
}
其中:role==Qt::DisplayRole:模型中的条目能够有不同的角色,这样可以在不同的情况下提供不同的数据。例如,
Qt::DisplayRole 用来存取视图中显示的文字,角色由枚举类Qt::ItemDataRole定义。
下表列出了Item主要的角色及其描述。
常量 | 描述 |
---|---|
Qt::DisplayRole | 显示文字 |
Qt::DecorationRole | 绘制装饰数据(通常是图标) |
Qt::EditRole | 在编辑器中编辑的数据 |
Qt::ToolTipRole | 工具提示 |
Qt::StatusTipRole | 状态栏提示 |
Qt::WhatsThisRole | What’s This文字 |
Qt::SizeHintRole | 尺寸提示 |
Qt::FontRole | 默认代理的绘制使用的字体 |
Qt::TextAlignmentRole | 默认代理的对齐方式 |
Qt::BackgroundRole | 默认代理的背景画刷 |
Qt::ForegroundRole | 默认代理的前景画刷 |
Qt::CheckStateRole | 默认代理的检查框状态: |
Qt::UserRole | 用户自定义的数据的起始位置 |
headerData()函数返回固定的表头数据,设置水平表头的标题,具体代码如下:
QVariant ModelEx::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
return header[section];
return QAbstractTableModel::headerData(section,orientation,role);
}
(3)在源文件"main.cpp”中,将模型和视图关联,具体代码如下:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ModelEx modelEx; //新建ModelEx模型对象
QTableView view; //新建QTableView 视图对象
view.setModel(&modelEx); //将模型与视图关联
//设定视图的各个参数
view.setWindowTitle(QObject::tr("modelEx"));
view.resize(400,400);
view.show();
return a.exec();
}
(4)、运行结果如上图所示。
实现自定义的View,可继承自QAbstractItemView类,对所需的纯虚函数进行重定义与实现,对于QAbstractItemView类中
的纯虚函数,在子类中必须进行重定义,但不一定要实现,可根据需要选择实现。
通过利用 自定义的View,实现一个对TableModel的表格数据进行显示的柱状统计图例子,以此介绍如何应用自定义的
(1) 新建Qt Gui应用 ,选择Qt Widgets Application,项目名称为“ViewEx”, 基类选择“ QMainWindow”,类名命
名为“mainwindow”,勾选“创建界面”复选框的选中状态。
(2)完成主窗体,以便显示View的内容。MainWindow类继承自QMainWindow类,作为主窗体。
以下是头文件“mainwindow.h”的类声明具体代码:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void createAction();
void createMenu();
void setupModel();
void setupView();
void openFile(QString);
private:
QMenu *fileMenu;
QAction *openAct;
QStandardItemModel *model;
QTableView *table;
QSplitter *splitter;
HistogramView *histogram;
public slots:
void slotOpen();
};
(3)同时,需要在头文件中添加以下头文件:
#include
#include
#include
#include
#include
#include
#include "histogramview.h"
(4)、下面是源文件"mainwindow.cpp"中的具体代码:
a、构造、析释mainwindow,定义mainwindow类的几个重要函数:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
createAction();
createMenu();
setupModel();
setupView();
setWindowTitle(tr("View Example"));
resize(600,600);
}
MainWindow::~MainWindow()
{
}
void MainWindow::createAction()
{
openAct = new QAction(tr("打开"),this);
connect(openAct,SIGNAL(triggered()),this,SLOT(slotOpen()));
}
void MainWindow::createMenu()
{
fileMenu = new QMenu(tr("文件"),this);
fileMenu->addAction(openAct);
menuBar()->addMenu(fileMenu);
}
b、setupModel()函数新建-一个Model,setupView()函数新建一个View,并设置表头数据,其具体实现代码如下:
void MainWindow::setupModel()
{
model = new QStandardItemModel(4,4,this);
model->setHeaderData(0,Qt::Horizontal,tr("部门"));
model->setHeaderData(1,Qt::Horizontal,tr("男"));
model->setHeaderData(2,Qt::Horizontal,tr("女"));
model->setHeaderData(3,Qt::Horizontal,tr("退休"));
}
void MainWindow::setupView()
{
splitter = new QSplitter;
splitter->setOrientation(Qt::Vertical);
histogram = new HistogramView(splitter);
histogram->setModel(model);
table = new QTableView; //新建一个QTableView对象
table->setModel(model); //为新建的QTableView对象设置相同的Model
QItemSelectionModel *selectionModel=new QItemSelectionModel(model); //
table->setSelectionModel(selectionModel);
histogram->setSelectionModel(selectionModel);
splitter->addWidget(table);
splitter->addWidget(histogram);
setCentralWidget(splitter);
//连接选择模型的selectionChanged0信号与QTableView 对象的selectionChanged()槽函数,以便使自定义的HistogramView对象中的选择变化能够反映到QTableView对象的显示中。
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),table,SLOT(selectionChanged(QItemSelection,QItemSelection)));
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),histogram,SLOT(selectionChanged(QItemSelection,QItemSelection)));
}
(5)、运行程序,效果如上图所示。