本示例来自于Qt5.6.2
本篇主要看WriteRegisterModel这个模型类是怎么实现的
涉及知识点主要是model/view中的model
WriteRegisterModel有四个变量
public:
int m_number; //读写的个数
int m_address; //读写的起始位置
QBitArray m_coils; //Coils
QVector m_holdingRegisters;//HoldlingRegister
Modbus 一共有四组变量,其中可写的是 Coils,HoldingRegisters
该示例每组变量一共有10个值,m_address是起始位置,m_number是从起始位置开始读几个。
该模型是二维的,因此除了构造函数以外,只需要实现三个函数:rowCount(),columnCount()和data()函数。如果是字符串列表那样一维的,则只需实现rowCount()和data()函数。如果模型是树状图那样的层次结构,还需要实现index()和parent()函数.
其中rowCount(),columnCount()返回模型的行列数,data()返回指定模型索引的数据项。
也可以实现headerData()函数,它是显示表头的。
先看看构造函数
WriteRegisterModel::WriteRegisterModel(QObject *parent)
: QAbstractTableModel(parent),
m_coils(RowCount, false), m_holdingRegisters(RowCount, 0u)
{
}
还有rowCount(),columnCount()
int WriteRegisterModel::rowCount(const QModelIndex &/*parent*/) const
{
return RowCount;
}
int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const
{
return ColumnCount;
}
这几个没啥好说的
data()函数:
QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
return QVariant();//要求索引有效,行列号在大小范围之内
Q_ASSERT(m_coils.count() == RowCount);
Q_ASSERT(m_holdingRegisters.count() == RowCount);
if (index.column() == NumColumn && role == Qt::DisplayRole)//NumColumn被枚举为0
return QString::number(index.row());//第一列返回行号
if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coils
return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;//返回是否被选中(at返回该位置的值)
else if (index.column() == HoldingColumn && role == Qt::DisplayRole) //holding registers
return QString("0x%1").arg(QString::number(m_holdingRegisters.at(index.row()), 16));//以十六进制返回指定位置的值
return QVariant();
}
headerData()函数:
QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
switch (section) {
case NumColumn:
return QStringLiteral("#");
case CoilsColumn:
return QStringLiteral("Coils ");
case HoldingColumn:
return QStringLiteral("Holding Registers");
default:
break;
}
}
return QVariant();
}
为了使模型可编辑,需要另外两个函数flags()和setData()
flags():
Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
{
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
return QAbstractTableModel::flags(index);
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))//如果index小于起始位置,大于结束位置,则返回0
flags &= ~Qt::ItemIsEnabled;//?不懂返回为什么这么写?
if (index.column() == CoilsColumn) //coils
return flags | Qt::ItemIsUserCheckable;
if (index.column() == HoldingColumn) //holding registers
return flags | Qt::ItemIsEditable;
return flags;
}
委托在创建编辑器以前会检测项目是否是可编辑的,模型必须让委托知道它的项目是可编辑的,这里为模型中的每一个项目返回一个正确的标识来达到这个目的。
setData():
bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
return false;
Q_ASSERT(m_coils.count() == RowCount);//如果()里为0,返回警告
Q_ASSERT(m_holdingRegisters.count() == RowCount);
if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coils
auto s = static_cast(value.toUInt());//?这里的类型转换为什么要这样?
s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());
emit dataChanged(index, index);
return true;
}
if (index.column() == HoldingColumn && Qt::EditRole) { // holding registers
bool result = false;
quint16 newValue = value.toString().toUShort(&result, 16);//?还有这里?
if (result)
m_holdingRegisters[index.row()] = newValue;
emit dataChanged(index, index);
return result;
}
return false;
}
注意,我们并不需要知道委托是怎样执行真正的编辑操作的,而只需要为委托向模型中设置一条途径,这个是通过setData()函数实现的(个人理解,这里不需要知道是怎样执行的是指:比如我没有找到setData函数被调用的地方,应该就是这种吧)
当数据被设置后,模型必须让视图知道有数据已经改变了,这就是最后都有个emit dataChanged(index, index);的意义
setData函数就是给这个item设置一个QVariant的值,但是,这个函数有两个参数,第一个QVariant自然是需要设置的值,另一个是一个int型数据,Qt中把这个称为role角色,所谓角色,是指设定进item的这个Qvariant所扮演的角色,实际就是对设定值的标定,因为item可以设置许多值,这就需要一个用以区分的标志,这个区分标志就叫角色。