官方文档,有改动。

模型/视图组件之间的功能分离允许创建可以利用现有视图的模型。

QAbstractItemModel 类提供了一个足够灵活的接口,以支持以分层结构排列信息的数据源,允许以某种方式插入、删除、修改或排序数据。它还提供对拖放操作的支持。

QAbstractListModel 和 QAbstractTableModel 类为更简单的非分层数据结构的接口提供支持,并且更容易用作简单列表和表模型的起点。

设计模型

在为现有数据结构创建新模型时,重要的是要考虑应该使用哪种类型的模型来提供数据接口。

如果数据结构可以表示为一个列表或项目表格,可以将 QAbstractListModel QAbstractTableModel 子类化,因为这些类为许多函数提供了合适的默认实现。

但是,如果底层数据结构只能用层次树结构表示,就需要对QAbstractItemModel进行子类化。

在本节中,我们实现了一个基于字符串列表的简单模型,QAbstractListModel 提供是一个理想的构建基类。

只读模型示例

这里实现的模型是一个基于标准 QStringListModel 类的简单、非分层、只读的数据模型。它有一个 QStringList 作为其内部数据源。

在实现模型时,重要的是要记住 QAbstractItemModel 本身不存储任何数据,它仅提供视图用来访问数据的接口

类声明如下:

class StringListModel : public QAbstractListModel
 {
     Q_OBJECT

 public:
     StringListModel(const QStringList &strings, QObject *parent = nullptr)
         : QAbstractListModel(parent), stringList(strings) {}

     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
     QVariant data(const QModelIndex &index, int role) const override;
     QVariant headerData(int section, Qt::Orientation orientation,
                         int role = Qt::DisplayRole) const override;

 private:
     QStringList stringList;
 };

除了模型的构造函数,我们只需要实现两个函数:

  • rowCount() 返回模型中的行数
  • data() 返回与指定模型索引对应的数据项。

还可以实现 headerData() 提供一些显示在其标题中的内容。

如果的模型是分层的,还必须实现 index() 和 parent() 函数。

字符串列表内部存储在 stringList 私有成员变量中。

模型尺寸

行数和列数:

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

 int StringListModel::columnCount(const QModelIndex &parent) const
 {
     return 2;
 }

模型头和数据

data() 函数负责返回对应于 index 参数的数据项:

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)
         return stringList.at(index.row());
     else
         return QVariant();
 }

某些视图,例如 QTreeView 和 QTableView,能够显示标题以及项目数据。可以通过子类化 headerData() 函数来提供有关标题的信息:

QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
                                      int role) const
 {
     if (role != Qt::DisplayRole)
         return QVariant();

     if (orientation == Qt::Horizontal)
         return QStringLiteral("Column %1").arg(section);
     else
         return QStringLiteral("Row %1").arg(section);
 }

可编辑的模型

只读模型显示了如何向用户呈现简单的选择,但对于许多应用程序,可编辑列表模型更有用。 我们可以修改只读模型,通过更改我们为只读实现的 data() 函数,并通过实现两个额外的函数:flags() 和 setData() 来使项目可编辑。 以下函数添加到类中:

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


 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, {role});
         return true;
     }
     return false;
 }

设置数据后,模型必须让视图知道某些数据已更改。这是通过发出 dataChanged() 信号来完成的。由于只有一项数据发生了变化,因此信号中指定的项目范围仅限于一个模型索引。

还需要更改 data() 函数:

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();
 }

汇总:

#ifndef STRINGLISTMODEL_H
#define STRINGLISTMODEL_H

#include <QAbstractListModel>

class StringListModel : public QAbstractListModel
{
    Q_OBJECT

public:
    StringListModel(const QStringList &strings, QObject *parent = nullptr)
        : QAbstractListModel(parent), stringList(strings){}

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;

    QVariant data(const QModelIndex &index, int role) const override;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    bool setData(const QModelIndex &index, const QVariant &value,
                     int role = Qt::EditRole) override;
    bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
    bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;

private:
    QStringList stringList;
};

#endif // STRINGLISTMODEL_H
#include "stringlistmodel.h"

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

int StringListModel::columnCount(const QModelIndex &parent) const
{
    return 2;
}

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 tr("第 %1 列").arg(section);
    else
        return tr("第 %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, {role});
        return true;
    }
    return false;
}

bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    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 &parent)
{
    beginRemoveRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        stringList.removeAt(position);
    }

    endRemoveRows();
    return true;
}

测试:

QStringList numbers;
    numbers << "One" << "Two" << "Three" << "Four" << "Five";

    StringListModel * model = new StringListModel(numbers);
    QTableView view;
    view.setModel(model);
    view.show();

 

QT 自定义progress_ide

 

QT 自定义progress_List_02

改一下data(),修改项目出现位置:

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)
    {
        if(index.column() == 0)
            if(index.row() % 2 == 0)
                return stringList.at(index.row());
            else
                return QVariant();
        else
            if(index.row() % 2 == 0)
                return QVariant();
            else
                return stringList.at(index.row());
    }
    else
        return QVariant();
}

QT 自定义progress_QT 自定义progress_03