Qt学习之路---模型与自定义模型

注意:以下可能有写错的地方,请见谅
本文尽量写的详细,为了以后的翻阅

模型

提到模型就要说视图和数据,它们三者的关系是相辅相成的。
数据负责提供,模型负责中转存放,视图负责显示,这是它们的关系。
模型中有它自己的索引(QModelIndex),由很多索引构成了一个模型。
如果需要获取数据必须要通过索引来获取设置。
简单的不多说。下面是自定义模型时间

自定义模型

本文对QAbstractTableModel进行说明
知己知彼方能百战百胜。假设我们已经有了数据。
但是怎么放进模型里面呢。下面我们来说一下模型的简单构造

构造自定义模型

  • 首先我们要新建一个类并且继承QAbstractTableModel
#ifndef MYMODELITEMMODEL_H
#define MYMODELITEMMODEL_H
#include <QAbstractTableModel>

class MyModelItemModel : public QAbstractTableModel
{
public:
    MyModelItemModel(QObject *parent);

    QVariant data(const QModelIndex &index, int role) const;
    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;

    bool getData(QVariant &OtherData);
private:
    QList<QList<QVariant>> m_data;
};

#endif // MYMODELITEMMODEL_H

首先我们要说明一下,自定义模型需要自己创建一个数据的载体,我们选择了QList<QList<QVariant>> *m_data一个二维数组来盛放表格的数据。这个数据的载体大家可以按照自己的需要来分别设置不同的载体,例如QList *m_data都可以的。

我们既然有了数据,肯定要首先把数据塞到模型里面这样才对嘛。
那我们就先实现数据加载进入模型。
既然这样我们就需要首先实现加载数据的函数bool getData(QVariant &OtherData)

bool MyModelItemModel::getData(QVariant &OtherData)
{
    // 因为咱们的盛放数据的格式为QList<QList<Qvariant>>;
    // 所以要把这个OtherData的数据逐层分解
    QVariantList varRows = OtherData.toList(); // 获得二维数组
    if (varRows.isEmpty())
        return false;

    for (int i=0; i<varRows.size(); i++){
        QVariantList buf = varRows.at(i).toList(); // 获得二维数组下某个一维数组
        m_data.push_back(buf); // 把其他的数据压入自己构造的数据源
    }
}

好,当我们的模型已经注入了新鲜的数据,剩下的事情肯定就是要把数据显示到视图里面来

视图里面通常用肉眼可以观察到一些东西:

  • 列(标题,数量)
  • 行(行号)
  • 数据
    通常都是这三种,那我们就围绕这三种来实现函数

现在我们模型里面已经有了数据,就可以实现模型返回给视图的数据了

QVariant MyModelItemModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    int column = index.column();

    if (!index.isValid()) // 如果index是无效的则返回无效的变体型
        return QVariant();
    if (role == Qt::DisplayRole){ // 判断角色,此角色有很多种,可以自己查看帮助来选择需要的
        if (row >= m_data.size()) // 如果index的row大于数据的row
            return QVariant();
        if (column >= m_data.at(row).size()) // 如果index.column大于数据的最大column
            return QVariant();
        return m_data.at(row).at(column); // 都ok了则返回数据
    }else{
        return QVariant(); // 如果角色不是我们想要的则过滤掉
    }
}

这样就实现了返回的数据,并且我们自己也剩下了我们想要的数据,不想要的都已经被过滤掉。

虽然有数据了,但是视图还是不知道要新建多少行号和列号正好来满足模型所需,不产生浪费
所以我们就要实现rowCount(const QModelINdex)column(const QModelIndex)

int MyModelItemModel::rowCount(const QModelIndex &parent) const
{
    return m_data.size();
}
int MyModelItemModel::columnCount(const QModelIndex &parent) const
{
    if (m_data.isEmpty())
        return -1;
    else
        return m_data.at(0).size();
}

行和列的实现很简单,只需要返回模型的各种尺寸即可。

这样基本一个简单的模型就搞定了。大家可以试试啦。

我自己写了一个小例子,大家可以参照下

#include "widget.h"
#include <QApplication>
#include "mymodelitemmodel.h"
#include <QGridLayout>
#include <QTableView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QVariantList list;
    QVariantList buf;
    for (int i=1; i<=30; i++){
        for (int j=1; j<=30; j++){
            buf.append("app");
        }
        list.push_back(buf);
    }

    QVariant data(list);

    MyModelItemModel *model = new MyModelItemModel();
    model->getData(data);

    QTableView *view = new QTableView();
    view->setModel(model);

    QGridLayout *layout = new QGridLayout();
    layout->addWidget(view);

    Widget w;
    w.setLayout(layout);
    w.show();

    return a.exec();
}

最后

有时候我们还想编辑模型,那么我们可以再自定义模型里面h文件声明

    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role);

其中Qt::ItemFlags flags(const QModelIndex &index) const;是表示索引的属性,基类实现了Qt::ItemIsSelectableQt::ItemIsEnabled两个默认的属性,如果我们需要编辑模型,首先要设置索引的属性

Qt::ItemFlags MyModelItemModel::flags(const QModelIndex &index) const
{
    if (! index.isValid())
        return Qt::ItemIsEnabled;
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; // 索引默认的属性需要添加的编辑属性
}

这样我们就设置好索引的属性了。
现在设置好属性了,双击表格内的索引也可以被修改了,但是你会发现修改的内容没有被正确显示。这是因为数据源里面的对应索引的数据没有被改变。我们因此还需要实现bool setData(const QModelIndex &index, const QVariant &value, int role)

bool MyModelItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (! index.isValid())
        return false;
        // 如果被修改的角色为editRolr,这个角色代表编辑角色
    if (role == Qt::EditRole){
        m_data[index.row()].replace(index.column(), value); // 我们需要先修改数据源里面的数据
        emit dataChanged(index, index); 
        return true;
    }
    return false;
}

这个emit dataChanged(index, index);是必须要发射的,如果你不发射这个数据修改的信号,视图是不会有所反应的,因为它不知道数据被修改了,咱们刚开始也说了,革命分工不同。所以我们发射这个信号,让视图来取数据。此时我们可以看看QVariant data(const QModelIndex &index, int role) const这个返回数据的函数

QVariant MyModelItemModel::data(const QModelIndex &index, int role) const
{
........ 
........ // 上面的代码省略了
    if (role == Qt::DisplayRole ){ // 判断角色,此角色有很多种,可以自己查看帮助来选择需要的
.......
......
}

此时if (role == Qt::DisplayRole)这个角色是以文本来显示数据的,但是我们编辑索引之后,索引的属性发生了变化,增加了一个Qt::ItemIsEditable,它个人的角色也产生了变化。所以我们需要添加上一个角色Qt::EditROle

    if (role == Qt::DisplayRole || role == Qt::EditRole)

这样就成功了!

总体来讲,刚接触模型,会发现细节有很多,我们稍一疏忽就会出错。所以我们要记住模型是以索引为单位干活,角色为单位显示,这样想的话会比较容易理解一点。

结束

博文如若写的有错误,请不吝指出,共同学习!

猜你喜欢

转载自blog.csdn.net/wayrboy/article/details/79576125
今日推荐