Qt之模型-视图编程(自定义模型)

简述

在构建一个模型类之前,需要先考虑清楚这个模型面对的是一个怎样的数据源,只有清楚数据源的特性,才能知道要利用哪个基类,实现哪些接口。比如,如果数据结构可以被表示为列表或者表格,我们可以通过子类化QAbstractListModel或者QAbstractTableModel来实现。如果底层数据结构是层级结构,我们就需要考虑子类化QAbstractItemModel。

这篇文章介绍的例子是实现一个简单的字符串列表模型,这个模型数据源是字符串列表。考虑到是列表类型,QAbstractListModel是个理想的选择。

一个只读模型

下面的模型基于QAbstractListModel实现。只提供了刚好让这个模型能运作的接口。它用一个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()来提供头部信息。

下面是每个方法的具体实现:

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 QStringLiteral("Column %1").arg(section);
    else
        return QStringLiteral("Row %1").arg(section);
}

提示: 上面所实现的接口都是虚函数。视图会自动调用这些接口来展示数据。

让模型可编辑

Qt之模型-视图编程(代理类)中我们了解到,编辑功能是由代理提供。代理在为项目创造一个编辑器之前会先判断该项目是否可编辑,判断方法是flags(),所以我们要重写该函数使得所有项目都为可编辑状态。重写方法如下:

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

      return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
  }

请记住, 我们不需要知道代理执行编辑的具体过程是怎样的,只需要向代理提供一个setData()接口用于将数据设置到数据源中。实现如下:

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

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

插入和移除行

插入和移除行也是可能的,注意: 这些函数需要自己调用才会执行。

实现如下:

  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 stringList;
    stringList << "床前明月光"
               << "疑是地上霜"
               << "举头望明月"
               << "低头思故乡";
    StringListModel *stringListModel = new StringListModel(stringList);
    QListView *listView = new QListView;
    listView->setModel(stringListModel);


    QGridLayout *gridLayout = new QGridLayout;
    gridLayout->addWidget(listView);

    this->setLayout(gridLayout);

引用

[1] Qt助手

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值