來自官方文檔,有改動。
模型/視圖元件之間的功能分離允許建立可以利用現有視圖的模型。
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();
}