官方文档,有改动。
模型/视图组件之间的功能分离允许创建可以利用现有视图的模型。
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();
改一下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();
}