6.qsqlquerymodel源码分析
目录
- 继承关系
- 入口
- 浅析qsqlquery
- 刷新数据
- 扩展列或者移除列以及取别名
- 读取数据与增减行
- 读取数据
- 下一章节:如何使用qsqlquerymodel 与 qtableview实现自定义表格
继承关系
qsqlquerymodel 继承与qabstracttablemodel
入口
负责填充数据
void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db) //无法配置绑定参数
void QSqlQueryModel::setQuery(const QSqlQuery &query) //非常友好,支持自定义query
浅析qsqlquery
你会发现提供的构造qsqlquery中如果
携带sql字符串语句则 if (!query.isEmpty()) q->exec(query);
自动执行,具体请看qInit()
所以在使用qsqlquery的过程中如果传入sql语句则不用手动执行
否则就需要自己执行exec
QSqlQuery::QSqlQuery(QSqlDatabase db)
{
d = QSqlQueryPrivate::shared_null();
qInit(this, QString(), db);
}
QSqlQuery::QSqlQuery(const QString& query, QSqlDatabase db)
{
d = QSqlQueryPrivate::shared_null();
qInit(this, query, db);
}
//1.提供的数据库连接无效则获取默认的数据库,且未开启
static void qInit(QSqlQuery *q, const QString& query, QSqlDatabase db)
{
QSqlDatabase database = db;
if (!database.isValid())
database = QSqlDatabase::database(QLatin1String(QSqlDatabase::defaultConnection), false);
if (database.isValid()) {
*q = QSqlQuery(database.driver()->createResult());
}
if (!query.isEmpty())
q->exec(query);
}
数据返回
sqlResult 是由不同的数据库驱动提供的
QSqlRecord QSqlQuery::record() const
{
QSqlRecord rec = d->sqlResult->record();
if (isValid()) {
for (int i = 0; i < rec.count(); ++i)
rec.setValue(i, value(i));
}
return rec;
}
QVariant QSqlQuery::value(int index) const
{
if (isActive() && isValid() && (index > -1))
return d->sqlResult->data(index);
qWarning("QSqlQuery::value: not positioned on a valid record");
return QVariant();
}
而为什么qsqlquery可以使用qsqlresult的protected东西,虽然不是相互继承关系
但因为在qsqlresult中声明了qsqlquery为友元类,所以依旧能够使用
但是外部是无法使用的
所以外部提供的 没什么太大的作用
const QSqlResult * result() const
实际
qsqlquery.record() 第一行是表头
所以执行next再获取才是数据库里面查询结果的第一行数据
setquery分析
所以我们了解了qsqlquery的处理机制,
我们就明白在执行qsqlquerymodel 的setquery
就是将已经存在数据的query填充到querymodel 模型中供外部使用
void QSqlQueryModel::setQuery(const QSqlQuery &query)
{
Q_D(QSqlQueryModel);
beginResetModel();
QSqlRecord newRec = query.record();
bool columnsChanged = (newRec != d->rec);
//设置列数
if (d->colOffsets.size() != newRec.count() || columnsChanged)
d->initColOffsets(newRec.count());
d->bottom = QModelIndex(); //最后一行与最后一列的坐标
d->error = QSqlError();
d->query = query;
d->rec = newRec; //表头
d->atEnd = true; //最后一行吗
//获取数据的方式是由下往上则
//这个模型不支持这样子的query
if (query.isForwardOnly()) {
d->error = QSqlError(QLatin1String("Forward-only queries "
"cannot be used in a data model"),
QString(), QSqlError::ConnectionError);
endResetModel();
return;
}
if (!query.isActive()) {
d->error = query.lastError();
endResetModel();
return;
}
//query是否存在数据
if (query.driver()->hasFeature(QSqlDriver::QuerySize) && d->query.size() > 0) {
d->bottom = createIndex(d->query.size() - 1, d->rec.count() - 1);
} else {
d->bottom = createIndex(-1, d->rec.count() - 1);
d->atEnd = false;
}
// fetchMore does the rowsInserted stuff for incremental models
fetchMore(); //
endResetModel();
queryChange();
}
不难看出初始化数据需要这样子的闭合关系
beginResetModel();
//-----填充数据域
endResetModel();
刷新数据
void QSqlQueryModel::fetchMore(const QModelIndex &parent)
{
Q_D(QSqlQueryModel);
if (parent.isValid())
return;
//预先刷新多少行,QSQL_PREFETCH=255
d->prefetch(qMax(d->bottom.row(), 0) + QSQL_PREFETCH);
}
//limit>255
void QSqlQueryModelPrivate::prefetch(int limit)
{
Q_Q(QSqlQueryModel);
// 如果确实是最后一行则不往下执行
if (atEnd || limit <= bottom.row() || bottom.column() == -1)
return;
QModelIndex newBottom;
const int oldBottomRow = qMax(bottom.row(), 0);
// try to seek directly
// 查看数据是否超过limit行,则新行先预加载到这里,
//一个预加载操作
if (query.seek(limit)) {
newBottom = q->createIndex(limit, bottom.column());
} else {
// have to seek back to our old position for MS Access
int i = oldBottomRow;
if (query.seek(i)) {
while (query.next())
++i;
newBottom = q->createIndex(i, bottom.column());
} else {
// empty or invalid query
newBottom = q->createIndex(-1, bottom.column());
}
atEnd = true; // this is the end.
}
if (newBottom.row() >= 0 && newBottom.row() > bottom.row()) {
q->beginInsertRows(QModelIndex(), bottom.row() + 1, newBottom.row());
bottom = newBottom; //更新最后一行坐标
q->endInsertRows();
} else {
bottom = newBottom;
}
}
可以看到执行插入操作
beginInsertRows();
//插入操作作用域
endInsertRows();
扩展列或者移除列以及取别名
不难发现就是往rect表头管理的record里面加入一个新的qsqlfield
//批量增加列
bool QSqlQueryModel::insertColumns(int column, int count, const QModelIndex &parent)
{
Q_D(QSqlQueryModel);
if (count <= 0 || parent.isValid() || column < 0 || column > d->rec.count())
return false;
beginInsertColumns(parent, column, column + count - 1);
for (int c = 0; c < count; ++c) {
QSqlField field;
field.setReadOnly(true);
field.setGenerated(false);
d->rec.insert(column, field);
if (d->colOffsets.size() < d->rec.count()) {
int nVal = d->colOffsets.isEmpty() ? 0 : d->colOffsets[d->colOffsets.size() - 1];
d->colOffsets.append(nVal);
Q_ASSERT(d->colOffsets.size() >= d->rec.count());
}
for (int i = column + 1; i < d->colOffsets.count(); ++i)
++d->colOffsets[i];
}
endInsertColumns();
return true;
}
//批量删除列
bool QSqlQueryModel::removeColumns(int column, int count, const QModelIndex &parent)
{
Q_D(QSqlQueryModel);
if (count <= 0 || parent.isValid() || column < 0 || column >= d->rec.count())
return false;
beginRemoveColumns(parent, column, column + count - 1);
int i;
for (i = 0; i < count; ++i)
d->rec.remove(column);
for (i = column; i < d->colOffsets.count(); ++i)
d->colOffsets[i] -= count;
endRemoveColumns();
return true;
}
//取别名
bool QSqlQueryModel::setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role)
{
Q_D(QSqlQueryModel);
if (orientation != Qt::Horizontal || section < 0 || columnCount() <= section)
return false;
if (d->headers.size() <= section)
d->headers.resize(qMax(section + 1, 16));
d->headers[section][role] = value;
emit headerDataChanged(orientation, section, section);
return true;
}
// 获取表头列名
QVariant QSqlQueryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_D(const QSqlQueryModel);
if (orientation == Qt::Horizontal) {
QVariant val = d->headers.value(section).value(role);
if (role == Qt::DisplayRole && !val.isValid())
val = d->headers.value(section).value(Qt::EditRole);
if (val.isValid())
return val;
if (role == Qt::DisplayRole && d->rec.count() > section && d->columnInQuery(section) != -1)
return d->rec.fieldName(section);
}
return QAbstractItemModel::headerData(section, orientation, role);
}
读取数据与增减行
因为是数据库查询的,我觉的没必要setData
我们只需要控制我们的列就行了
比如,我们可以在前面加一列—到时候传入个checkbox或者单选框
在加入一个选择管理器就可以做到选择功能
最后就是一列多个按键,我们也可以通过多增加列进行
model->insertColumns(0,1);
model->setHeaderData(0,Qt::Horizontal,QString("选择"));
动态列的字段数据
0: QSqlField("", , tableName: "(not specified)", generated: no, autoValue: false, readOnly: true) ""
读取数据
//返回行数据
QSqlRecord QSqlQueryModel::record(int row) const
{
Q_D(const QSqlQueryModel);
if (row < 0)
return d->rec;
QSqlRecord rec = d->rec;
for (int i = 0; i < rec.count(); ++i)
rec.setValue(i, data(createIndex(row, i), Qt::EditRole));
return rec;
}
//表头数据
QSqlRecord QSqlQueryModel::record() const
{
Q_D(const QSqlQueryModel);
return d->rec;
}
QVariant QSqlQueryModel::data(const QModelIndex &item, int role) const
{
Q_D(const QSqlQueryModel);
if (!item.isValid())
return QVariant();
QVariant v;
if (role & ~(Qt::DisplayRole | Qt::EditRole))
return v;
// 非原来的 则直接返回, 就是调用insertColumns加的则直接返回
if (!d->rec.isGenerated(item.column()))
return v;
//获取在query中实际位置
QModelIndex dItem = indexInQuery(item);
if (dItem.row() > d->bottom.row())
const_cast<QSqlQueryModelPrivate *>(d)->prefetch(dItem.row());
if (!d->query.seek(dItem.row())) {
d->error = d->query.lastError();
return v;
}
return d->query.value(dItem.column());
}
QModelIndex QSqlQueryModel::indexInQuery(const QModelIndex &item) const
{
Q_D(const QSqlQueryModel);
int modelColumn = d->columnInQuery(item.column());
if (modelColumn < 0)
return QModelIndex();
return createIndex(item.row(), modelColumn, item.internalPointer());
}
int QSqlQueryModelPrivate::columnInQuery(int modelColumn) const
{
if (modelColumn < 0 || modelColumn >= rec.count() || !rec.isGenerated(modelColumn) || modelColumn >= colOffsets.size())
return -1;
return modelColumn - colOffsets[modelColumn];
}
// 提供给委托使用的, 如果设置
QHash<int, QByteArray> QSqlQueryModel::roleNames() const
{
return QHash<int, QByteArray> {
{ Qt::DisplayRole, QByteArrayLiteral("display") }
};
}
而rolename有什么用?
主要用于在qml中可以使用属性名如model.name , model.display之类的
void initializeConstructor(QQmlAdaptorModelEngineData *const data)
{
QV4::ExecutionEngine *v4 = data->v4;
QV4::Scope scope(v4);
QV4::ScopedObject proto(scope, v4->newObject());
proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr);
proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, nullptr);
QV4::ScopedProperty p(scope);
typedef QHash<QByteArray, int>::const_iterator iterator;
for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
const int propertyId = propertyRoles.indexOf(it.value());
const QByteArray &propertyName = it.key();
QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName)));
QV4::ExecutionContext *global = v4->rootContext();
QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
p->setGetter(g);
p->setSetter(s);
proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
}
prototype.set(v4, proto);
}