QT5开发||12、QT5模型/视图结构

摘要

MVC设计模式是起源于Smalltalk的一种与用户界面相关的设计模式。通过使用此模型,可以有效地分离数据和用户界面。

MVC设计模式包括三个元素,表示数据的模型(Model)、表示用户界面的视图(View) 和定义了用户在界面上的操作控制(Controller)。

与MVC设计模式类似,Qt引入了模型/视图结构用于完成数据与界面的分离,即InterView框架。

*但不同的是,Qt的InterView框架中把视图和控制部件结合在一起,使得框架更为简洁。为了灵活地处理用户输入,InterView框架引入了代理(delegate)。通过使用代理,能够自定义数据条目(item)的显示和编辑方式。

一、Qt的模型、视图结构

Qt的模型/视图结构分为三部分,模型(Model)、视图(View) 和代理(Delegate)。

其中,模型与数据源通信,并为其他部件提供接口;而视图从模型中获得用来引用数据条目的模型索引(Model Index)。

在视图中,代理负责绘制数据条目,当编辑条目时,代理和模型直接进行通信。

模型/视图/代理之间通过信号和槽进行通信,见下图:
QT5开发||12、QT5模型/视图结构_第1张图片

●数据发生改变时,模型发出信号通知视图。
●用户对界面进行操作,视图发出信号。
●代理发出信号告知模型和视图编辑器目前的状态。

1、模型(Model)

InterView框架中的所有模型都基于抽象基类QAbstractItemModel类,此类由QProxyModel、QAbstractListModel、

QAbstractTableModel、 QAbstractProxyModel、QDirModel、QFileSystemModel、QHelpContentModel和

QStandardItemModel类继承。

其中,QAbstractListModel 类和QAbstractTableModel 类是列表和表格模型的抽象基类,如果需要实现列表或表格模

型,则应从这两个类继承。完成QStringList存储的QStringListModel继承自QAbstractListModel类,而与数据库有关的

QSqlQueryModel类继承自QAbstractTableModel 类; QAbstractProxyModel 类是代理模型的抽象类;QDirModel类是文件

和目录的存储模型。

2.视图(View)

InterView框架中的所有视图都基于抽象基类QAbstractItemView 类,此类由QColumnView、QHeaderView、QListView、

QTableView 和QTreeView类继承。

其中,QListView 类由QUndoView 类和QListWidget 类继承;QTableView 类由QTableWidget类继承; QTreeView 类由

QTreeWidget类继承。而QListWidget类、QTableWidget类和QTreeWidget类实际上已经包含了数据,是模型/视图集成

在一起的类。

3.代理( Delegate )

InterView框架中的所有代理都基于抽象基类QAbstractItemDelegate 类,此类由QItemDelegate和QStyledItemDelegate

类继承。其中,QItemDelegate 类由表示数据库中关系代理的QSqlRelationalDelegate类继承。

二、模型/视图类使用案例

介绍如何使用Qt中提供的QDirModel类、QTreeView 类、QListView 类和QTableView类。完成效果如下图所示。
QT5开发||12、QT5模型/视图结构_第2张图片

实现步骤:

(1) 新建Qt Gui应用 ,选择Qt Widgets Application,项目名称为“DtxModel”, 基类选择“ QMainWindow”,类名命

名为“DtxWindows”,勾选“创建界面”复选框的选中状态。 如下图所示。
QT5开发||12、QT5模型/视图结构_第3张图片(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类继承实现列表模型或表格模型。在数据库中通常需要首先将一些重复的文字字段使用数值代码保存,然后通过外键关联操作来查找其真实的含义,这一方法是为了避免冗余。

通过实现将数值代码转换为文字的模型来介绍如何使用自定义模型。此模型中保存了不同军种的各种武器。

实现效果如下图所示。
QT5开发||12、QT5模型/视图结构_第4张图片

实现步骤:

(1) 新建Qt Gui应用 ,选择Qt Widgets Application,项目名称为“DtxModelEx”, 基类选择“ QMainWindow”,类名命

名为“mainwindow”,勾选“创建界面”复选框的选中状态。 如下图所示。
QT5开发||12、QT5模型/视图结构_第5张图片(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)

实现自定义的View,可继承自QAbstractItemView类,对所需的纯虚函数进行重定义与实现,对于QAbstractItemView类中

的纯虚函数,在子类中必须进行重定义,但不一定要实现,可根据需要选择实现。

实例分析:

通过利用 自定义的View,实现一个对TableModel的表格数据进行显示的柱状统计图例子,以此介绍如何应用自定义的

View。 实现效果如下图。
QT5开发||12、QT5模型/视图结构_第6张图片

实现步骤:

(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)、运行程序,效果如上图所示。

至此,通过三个案例将Qt的模型与视图结构就介绍完毕了!

你可能感兴趣的:(GUI,qt,c++)