在Qt Quick 视图中使用 C++ 模型
自定义 C++ 模型中提供的数据
模型可以用 C++ 定义,然后提供给 QML。这对于向 QML 公开现有 C++ 数据模型或其他复杂数据集非常有用。
C++ 模型类可定义为QStringList 、QVariantList 、QObjectList 或QAbstractItemModel 。前三种可用于公开较简单的数据集,而QAbstractItemModel 可为较复杂的模型提供更灵活的解决方案。
下面的视频教程将带您了解将 C++ 模型公开到 QML 的整个过程:
基于 QStringList 的模型
模型可以是一个简单的QStringList ,通过modelData角色提供列表内容。
下面是一个ListView ,它有一个委托,使用modelData
角色引用其模型项的值:
ListView { width: 100 height: 100 required model delegate: Rectangle { required property string modelData height: 25 width: 100 Text { text: parent.modelData } } }
Qt XML 应用程序可以加载此 QML 文档,并将myModel
的值设置为QStringList :
QStringList dataList = { "Item 1", "Item 2", "Item 3", "Item 4" }; QQuickView view; view.setInitialProperties({{ "model", QVariant::fromValue(dataList) }});
此示例的完整源代码位于 Qt install 目录中的examples/quick/models/stringlistmodel。
注意: 视图无法知道QStringList 的内容发生了变化。如果QStringList 发生变化,则需要通过再次设置视图的model
属性来重置模型。
基于 QVariantList 的模型
模型可以是一个单一的QVariantList ,它通过modelData角色提供列表的内容。
API 的工作原理与QStringList 相似,如上一节所示。
注意: 视图无法知道QVariantList 的内容是否已更改。如果QVariantList 发生变化,就必须重置模型。
基于 QObjectList 的模型
QObject* 值的列表也可用作模型。QList<QObject*> 将列表中对象的属性作为角色提供。
下面的应用程序创建了一个DataObject
类,其中包含Q_PROPERTY 值,当QList<DataObject*> 暴露给 QML 时,这些值将作为命名的角色被访问:
class DataObject : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) ... }; int main(int argc, char ** argv) { QGuiApplication app(argc, argv); const QStringList colorList = {"red", "green", "blue", "yellow"}; const QStringList moduleList = {"Core", "GUI", "Multimedia", "Multimedia Widgets", "Network", "QML", "Quick", "Quick Controls", "Quick Dialogs", "Quick Layouts", "Quick Test", "SQL", "Widgets", "3D", "Android Extras", "Bluetooth", "Concurrent", "D-Bus", "Gamepad", "Graphical Effects", "Help", "Image Formats", "Location", "Mac Extras", "NFC", "OpenGL", "Platform Headers", "Positioning", "Print Support", "Purchasing", "Quick Extras", "Quick Timeline", "Quick Widgets", "Remote Objects", "Script", "SCXML", "Script Tools", "Sensors", "Serial Bus", "Serial Port", "Speech", "SVG", "UI Tools", "WebEngine", "WebSockets", "WebView", "Windows Extras", "XML", "XML Patterns", "Charts", "Network Authorization", "Virtual Keyboard", "Quick 3D", "Quick WebGL"}; QList<QObject *> dataList; for (const QString &module : moduleList) dataList.append(new DataObject("Qt " + module, colorList.at(rand() % colorList.length()))); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); view.setInitialProperties({{ "model", QVariant::fromValue(dataList) }}); ...
QObject* 可作为modelData
属性。为方便起见,对象的属性也直接在委托的上下文中提供。在这里,view.qml
引用了ListView 委托中的DataModel
属性:
ListView { id: listview width: 200; height: 320 required model ScrollBar.vertical: ScrollBar { } delegate: Rectangle { width: listview.width; height: 25 required color required property string name Text { text: parent.name } } }
请注意color
属性的使用。您可以通过在派生类型中将现有属性声明为required
来要求这些属性。
此示例的完整源代码位于 QtQuick安装目录下的examples/quick/models/objectlistmodel中。
注意:视图无法知道QList 的内容是否已更改。如果QList 发生变化,则有必要通过再次设置model
属性来重置模型。
QAbstractItemModel 子类
可以通过子类化QAbstractItemModel 来定义模型。如果您的模型比较复杂,而其他方法又无法支持,那么这是最好的方法。QAbstractItemModel 还能在模型数据改变时自动通知 QML 视图。
QAbstractItemModel 子类的角色可以通过重新实现QAbstractItemModel::roleNames() 暴露给 QML。
下面是一个带有QAbstractListModel 子类AnimalModel
的应用程序,它公开了类型和大小角色。它重新实现了QAbstractItemModel::roleNames() 以公开角色名称,这样就可以通过 QML 访问它们:
class Animal { public: Animal(const QString &type, const QString &size); ... }; class AnimalModel : public QAbstractListModel { Q_OBJECT public: enum AnimalRoles { TypeRole = Qt::UserRole + 1, SizeRole }; AnimalModel(QObject *parent = nullptr); ... }; QHash<int, QByteArray> AnimalModel::roleNames() const { QHash<int, QByteArray> roles; roles[TypeRole] = "type"; roles[SizeRole] = "size"; return roles; } int main(int argc, char ** argv) { QGuiApplication app(argc, argv); AnimalModel model; model.addAnimal(Animal("Wolf", "Medium")); model.addAnimal(Animal("Polar bear", "Large")); model.addAnimal(Animal("Quoll", "Small")); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); view.setInitialProperties({{"model", QVariant::fromValue(&model)}}); ...
该模型由访问类型和大小角色的ListView 委托显示:
ListView { width: 200; height: 250 required model delegate: Text { required property string type required property string size text: "Animal: " + type + ", " + size } }
当模型发生变化时,QML 视图会自动更新。请记住,模型必须遵循模型更改的标准规则,并在模型更改时通过QAbstractItemModel::dataChanged(),QAbstractItemModel::beginInsertRows() 等通知视图。有关详细信息,请参阅模型子类化参考资料。
此示例的完整源代码位于 Qt install 目录中的examples/quick/models/abstractitemmodel中。
QAbstractItemModel QML 提供了一个表格层次结构,但目前 QML 提供的视图只能显示列表数据。为了显示分层模型的子列表,请使用 QML 类型,它提供了以下属性和函数,可与 类型的列表模型一起使用:DelegateModel QAbstractItemModel
- hasModelChildren角色属性,用于确定节点是否有子节点。
- DelegateModel::rootIndex 允许指定根节点
- DelegateModel::modelIndex() 返回一个QModelIndex ,可将其分配给DelegateModel::rootIndex
- DelegateModel::parentModelIndex() 返回一个QModelIndex ,可将其分配给DelegateModel::rootIndex
SQL 模型
Qt SQL 提供了支持 SQL 数据模型的 C++ 类。这些类在底层 SQL 数据上透明地工作,减少了对基本 SQL 操作(如创建、插入或更新)运行 SQL 查询的需要。有关这些类的更多详情,请参阅使用 SQL 模型类。
虽然 C++ 类提供了操作 SQL 数据的完整功能集,但它们不提供对 QML 的数据访问。因此,你必须把 C++ 自定义数据模型作为这些类的子类来实现,并把它作为类型或上下文属性暴露给 QML。
只读数据模型
自定义模型必须重新实现以下方法,以便从 QML 只读访问数据:
- roleNames() 向 QML 前端公开角色名称。例如,以下版本将所选表的字段名作为角色名返回:
QHash<int, QByteArray> SqlQueryModel::roleNames() const { QHash<int, QByteArray> roles; // record() returns an empty QSqlRecord for (int i = 0; i < this->record().count(); i ++) { roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8()); } return roles; }
- data() 向 QML 前端公开 SQL 数据。例如,以下实现返回给定模型索引的数据:
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const { QVariant value; if (index.isValid()) { if (role < Qt::UserRole) { value = QSqlQueryModel::data(index, role); } else { int columnIdx = role - Qt::UserRole - 1; QModelIndex modelIndex = this->index(index.row(), columnIdx); value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole); } } return value; }
QSqlQueryModel 类足以实现表示 SQL 数据库中数据的自定义只读模型。聊天教程示例很好地演示了这一点,它实现了一个自定义模型,可从 SQLite 数据库中获取联系人详细信息。
可编辑数据模型
QSqlTableModel 实现了 setData(),如下所述。
根据模型使用的EditStrategy ,更改要么会排队等待稍后提交,要么会立即提交。
您还可以通过调用QSqlTableModel::insertRecord() 向模型中插入新数据。在下面的示例代码段中,QSqlRecord ,并将其添加到模型中:
... QSqlRecord newRecord = record(); newRecord.setValue("author", "John Grisham"); newRecord.setValue("booktitle", "The Litigators"); insertRecord(rowCount(), newRecord); ...
向 QML 公开 C++ 数据模型
上述示例使用视图上的必要属性直接在 QML 组件中设置模型值。另一个办法是把 C++ 模型类注册为 QML 类型(见从 C++ 定义 QML 类型)。这样,模型类就能直接作为 QML 中的类型创建:
C++ | class MyModel : public QAbstractItemModel { Q_OBJECT QML_ELEMENT // [...] } |
QML | MyModel { id: myModel } |
有关用 C++ 编写 QML 类型的详情,请参阅用 C++ 编写 QML扩展。
更改模型数据
除了roleNames()
和data()
之外,可编辑模型必须重新实现setData 方法,以保存对现有模型数据的更改。下面版本的方法会检查给定的模型索引是否有效,以及role
是否等于Qt::EditRole :
bool EditableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && role == Qt::EditRole) { // Set data in model here. It can also be a good idea to check whether // the new value actually differs from the current value if (m_entries[index.row()] != value.toString()) { m_entries[index.row()] = value.toString(); emit dataChanged(index, index, { Qt::EditRole, Qt::DisplayRole }); return true; } } return false; }
注意: 保存更改后发出dataChanged() 信号很重要。
与 C++ 项目视图(如QListView 或QTableView )不同,setData()
方法必须在适当的时候从 QML 委托中明确调用。只需给相应的模型属性赋一个新值即可。
ListView { anchors.fill: parent model: EditableModel {} delegate: TextField { width: ListView.view.width text: model.edit onAccepted: model.edit = text } }
注意: edit
角色等于Qt::EditRole 。有关内置角色名称,请参阅roleNames() 。不过,现实生活中的模型通常会注册自定义角色。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.