我的QT Creator学习笔记(三十)——模型/视图编程之模型类

Qt中的模型/视图架构就是用来实现大量数据的存储、处理及其显示的。

参考文献:《Qt Creator 快速入门》第三版 霍亚飞编著

模型/视图架构

MVC(Model-View-Controller)是一种起源于Smalltalk的设计模式,经常用于创建用户界面。MVC包含了3个组件:模型(Model)是应用对象,用来表示数据;视图(View)是模型的用户界面,用来显示数据;控制(controller)定义了用户界面对用户输入的反应。

模型/视图框架中的众多类可以分为3组:模型、视图和委托,它们之间使用信号和槽来实现通信:

》当数据源的数据发生改变时,模型发出信号告知视图;

》当用户与显示的项目交互时,视图发出信号来提供交互信息;

》当编辑项目时,委托发出信号,告知模型和视图编辑器状态。

一、模型类

在模型/视图架构中,模型提供了一个标准的接口供视图和委托来访问数据。这个标准接口使用QAbtructItemModel类来定义。

常见的三种模型分别是列表模型、表格模型和树模型

模型索引

为了确保数据的表示与数据的获取相分裂,Qt引入了模型索引的概念。每一块可以通过模型获取的数据都使用一个模型索引来表示,视图和委托都使用这些索引来请求数据并显示。

模型索引由QModelIndex类提供,它是对一块数据的临时引用,可以用来检索或者修改模型中的数据。如果要获得一个数据项的模型索引,则必须指定模型的3个属性:行号、列号和父项的模型索引。

QModelIdex index=mode->index(row,colum,parent);

行号和列号都是从0开始,顶层数据项可以使用QModelIndex()作为父项索引。

如下图Tree Model中的A、B、C 3项的模型索引可以使用如下代码获得。


 

    QModelIndex indexA=model->index(0,0,QModelIndex());
    QModelIndex indexB=model->index(1,0,indexA);
    QModelIndex indexC=model->index(2,0,QModelIndex());

项角色

模型中的数据项可以作为各种角色在其他组件中使用,允许为不同的情况提供不同类型的数据。通常情况下数据项目包含了一些不同角色的数据,这些标准的角色由Qt::ItemDataRole来定义。

二、使用标准项模型QStandardItemMoodel的例子

QStandardItemMoodel类提供了一个通用的模型来存储自定义数据。QStrandardItemModel中的项由QStandardItem类提供,该类可以使用setIcon等函数来指定图标等属性,也可以使用setData函数来设置这些属性。见下面例子。

新建空的Qt项目modelview1,在项目文件中添加“QT += widgets",添加main.cpp文件,更改其内容如下

#include <QApplication>
#include <QFileSystemModel>
#include <QTreeView>
#include <QListView>
#include <QStandardItemModel>
#include <QDebug>

int main(int argc,char* argv[])
{
     QApplication app(argc,argv);
    //创建标准项模型
    QStandardItemModel model;
    //获取模型的根项(Root Item),根项是不可见的
    QStandardItem* parentItem=model.invisibleRootItem();
    //创建标准项item0,并设置显示文本,图标和工具提示
    QStandardItem* item0=new QStandardItem;
    item0->setText("A");
    QPixmap pixmap0(50,50);
    pixmap0.fill("red");
    item0->setIcon(QIcon(pixmap0));
    item0->setToolTip("indexA");
    //将创建的标准项作为根项的子项
    parentItem->appendRow(item0);

    //将创建的标准项作为新的父项
    parentItem =item0;
    //创建新的标准项,将它作为item0的子项
    QStandardItem* item1=new QStandardItem;
    item1->setText("B");
    QPixmap pixmap1(50,50);
    pixmap1.fill("blue");
    item1->setIcon(QIcon(pixmap1));
    item1->setToolTip("indexB");
    parentItem->appendRow(item1);

    //创建新的标准项,这里使用了另一种方法来设置文本、图标和工具提示
    QStandardItem* item2=new QStandardItem;
    QPixmap pixmap2(50,50);
    pixmap2.fill("green");
    item2->setData("C",Qt::EditRole);
    item2->setData("indexC",Qt::ToolTipRole);
    item2->setData(QIcon(pixmap2),Qt::DecorationRole);
    parentItem->appendRow(item2);

    //在树视图中显示模型
    QTreeView view;
    //为视图指定模型
    view.setModel(&model);
    view.show();

    //获取item0的索引并输出item0的子项数目,然后输出了item1的显示文本和工具提示
    QModelIndex indexA=model.index(0,0,QModelIndex());
    qDebug()<<"indexA row count:"<<model.rowCount(indexA);
    QModelIndex indexB=model.index(0,0,indexA);
    qDebug()<<"indexB text:"<<model.data(indexB,Qt::EditRole).toString();
    qDebug()<<"indexB toolTip:"<<model.data(indexB,Qt::ToolTipRole).toString();
    return app.exec();
}

运行效果

三、自定义新的模型

当要为一个已经存在的数据结构创建新的模型时,需要考虑使用哪种类型的模型来为数据提供接口。如果数据结构可以表示为项目列表或者表格,那么可以子类化QAbstarctListModel或QAbstracTableModel。下面的例子,创建一个基于字符串列表的模型,以QAbstractListModel作为基类。对于自定的类要实现的几个关键点:

数据源:

添加QStringList 成员对象作为内部数据源。

显示:

1、实现父类纯虚接口QVariant data(const QModelIndex& index,int role)const;返回指定模型索引的数据

2、实现父类纯虚接口int rowCount(const QModelIndex& parent=QModelIndex())const;返回指定模型行数

3、重写父类虚接口QVariant headerData(int section, Qt::Orientation orientation,int role = Qt::DisplayRole) const;它可以在树和表格视图的表头显示一些内容(这一项非必须)

编辑功能:

1、重写父类虚接口Qt::ItemFlags flags(const QModelIndex &index) const;委托编辑器以前会检测项目是否是可编辑的,模型必须让委托知道它的项目是可编辑的,这里为模型中的每一个项目返回一个正确的标志来达到这个目的,具体看下面例子代码。

2、重写父类虚接口virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);该函数为委托向模型中设置数据提供了一条途径(委托并不需要知道怎样执行的)。当数据被设置后,模型必须让视图知道有数据已经改变了,在该函数中是通过发射dataChanged()信号实现的,详见代码部分。

增删功能

1、重写父类虚接口bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());实现增加行

2、重写父类虚接口 virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());实现删除行

 

最后自定义模型类StringListModel的头文件如下

#ifndef STRINGLISTMODEL_H
#define STRINGLISTMODEL_H
#include <QAbstractListModel>
#include <QStringList>

class StringListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    StringListModel(const QStringList& strings,QObject* parent=0)
        :QAbstractListModel(parent),stringList(strings){}
    int rowCount(const QModelIndex& parent=QModelIndex())const;
    QVariant data(const QModelIndex& index,int role)const;
    QVariant headerData(int section, Qt::Orientation orientation,int role = Qt::DisplayRole) const;
    Qt::ItemFlags flags(const QModelIndex& index)const;
    bool setData(const QModelIndex& index,const QVariant &value,int role=Qt::EditRole);
    bool insertRows(int position,int rows,const QModelIndex& index=QModelIndex());
    bool removeRows(int position,int rows,const QModelIndex& index=QModelIndex());
private:
;    QStringList stringList;
};

#endif // STRINGLISTMODEL_H

StringListModel类的实现如下

 

 

#include "stringlistmodel.h"


int StringListModel::rowCount(const QModelIndex &parent) const
{
    return stringList.count();
}

QVariant StringListModel::data(const QModelIndex &index, int role)const
{
    if(!index.isValid())
        return QVariant();
    if(index.row()==stringList.size())
        return QVariant();
    if(role==Qt::DisplayRole||role==Qt::EditRole)
        return stringList.at(index.row());
    else
        return QVariant();
}

QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role!=Qt::DisplayRole)
        return QVariant();
    if(orientation==Qt::Horizontal)
        return QString("Colum %1").arg(section);
    else
        return QString("Row %1").arg(section);
}

Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return Qt::ItemIsEnabled;
    return QAbstractItemModel::flags(index)|Qt::ItemIsEditable;
}

bool StringListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(index.isValid()&&role==Qt::EditRole)
    {
        stringList.replace(index.row(),value.toString());
        emit dataChanged(index,index);
        return true;
    }
    return false;
}

bool StringListModel::insertRows(int position, int rows, const QModelIndex &index)
{
    beginInsertRows(QModelIndex(),position,position+rows-1);
    for(int row=0;row<rows;row++)
    {
        stringList.insert(position,"");
    }
    endInsertRows();
    return true;
}

bool StringListModel::removeRows(int position, int rows, const QModelIndex &index)
{
    beginRemoveRows(QModelIndex(),position,position+rows-1);
    for(int row=0;row<rows;row++)
    {
        stringList.removeAt(position);
    }
    endRemoveRows();
    return true;
}

main.cpp中测试代码如下

#include <QApplication>
#include "stringlistmodel.h"
#include <QListView>
#include <QTableView>

int main(int argc,char* argv[])
{
    QApplication app(argc,argv);
    QStringList list;
    list<<"a"<<"b"<<"c";
    StringListModel model(list);
    QListView listView;
    listView.setModel(&model);
    listView.show();
    QTableView tableView;
    tableView.setModel(&model);
    tableView.show();
    model.insertRows(3,2);
    model.removeRows(0,1);
    return app.exec();
}

最后运行效果如下,可以编辑

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值