警告
本节包含从C++自动翻译到Python的代码片段,可能包含错误。
使用C++模型与Qt Quick视图¶
使用Qt Quick视图与在C++中定义的模型
自定义C++模型中提供的数据¶
模型可以在C++中定义,然后提供给QML使用。这对于将现有的C++数据模型或其他复杂的数据集暴露给QML非常有用。
一个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应用程序可以加载这个QML文档并将myModel
的值设置为一个QStringList:
dataList = { "Item 1", "Item 2", "Item 3", "Item 4" view = QQuickView() view.setInitialProperties({{ "model", QVariant.fromValue(dataList) }})
此示例的完整源代码可在Qt安装目录中的examples/quick/models/stringlistmodel找到。
注意
视图无法知道QStringList的内容是否发生了变化。如果QStringList发生变化,则需要通过再次设置视图的model
属性来重置模型。
基于QVariantList的模型¶
模型可能是一个单一的QVariantList,它通过modelData角色提供列表的内容。
API 的工作方式与 QStringList 相同,如前一节所示。
注意
视图无法知道QVariantList的内容是否发生了变化。如果QVariantList发生变化,则需要重置模型。
基于QObjectList的模型¶
QObject* 值的列表也可以用作模型。QList
以下应用程序创建了一个带有Q_PROPERTY值的DataObject
类,当QList
class DataObject(QObject): Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) ... if __name__ == "__main__": app = QGuiApplication(argc, argv) colorList = {"red", "green", "blue", "yellow"} 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() for module in moduleList: dataList.append(DataObject("Qt " + module, colorList.at(rand() % colorList.length()))) view = QQuickView() 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
来要求现有属性。
此示例的完整源代码可在Qt安装目录中的examples/quick/models/objectlistmodel找到。
注意:视图无法知道QList的内容是否发生了变化。如果QList发生变化,必须通过再次设置model
属性来重置模型。
QAbstractItemModel 子类¶
可以通过子类化QAbstractItemModel来定义一个模型。如果您有一个更复杂的模型,无法通过其他方法支持,这是最好的方法。当模型数据发生变化时,QAbstractItemModel还可以自动通知QML视图。
QAbstractItemModel子类的角色可以通过重新实现QAbstractItemModel::roleNames()暴露给QML。
这里有一个应用程序,它有一个名为 AnimalModel
的 QAbstractListModel 子类,它暴露了 type 和 sizes 角色。它重新实现了 QAbstractItemModel::roleNames() 来暴露角色名称,以便可以通过 QML 访问它们:
class Animal(): # public Animal(QString type, QString size) ... class AnimalModel(QAbstractListModel): Q_OBJECT # public AnimalRoles = { TypeRole = Qt.UserRole + 1, SizeRole AnimalModel(QObject parent = None) ... QHash<int, QByteArray> AnimalModel.roleNames() { QByteArray> = QHash<int,() roles[TypeRole] = "type" roles[SizeRole] = "size" return roles if __name__ == "__main__": app = QGuiApplication(argc, argv) model = AnimalModel() model.addAnimal(Animal("Wolf", "Medium")) model.addAnimal(Animal("Polar bear", "Large")) model.addAnimal(Animal("Quoll", "Small")) view = QQuickView() view.setResizeMode(QQuickView.SizeRootObjectToView) view.setInitialProperties({{"model", QVariant.fromValue(model)}}) ...
该模型通过访问type和size角色的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安装目录中的examples/quick/models/abstractitemmodel中找到。
QAbstractItemModel 提供了一个表格的层次结构,但目前由 QML 提供的视图只能显示列表数据。为了显示层次模型的子列表,请使用 DelegateModel QML 类型,它提供了以下属性和函数,用于与 QAbstractItemModel 类型的列表模型一起使用:
hasModelChildren 角色属性用于确定一个节点是否有子节点。
DelegateModel::rootIndex 允许指定根节点
DelegateModel::modelIndex() 返回一个可以分配给 DelegateModel::rootIndex 的 QModelIndex
DelegateModel::parentModelIndex() 返回一个可以分配给 DelegateModel::rootIndex 的 QModelIndex
SQL 模型¶
Qt 提供了支持 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() 返回一个空的 QSqlRecord for (int i = 0; i < this->record().count(); i ++) { roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8()); } return roles; }data() 用于将 SQL 数据暴露给 QML 前端。例如,以下实现返回给定模型索引的数据:
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); ...
将C++数据模型暴露给QML¶
上述示例使用视图上的必需属性直接在QML组件中设置模型值。另一种方法是将C++模型类注册为QML类型(参见从C++定义QML类型)。这使得模型类可以直接在QML中作为类型创建:
C++
class MyModel : public QAbstractItemModel { Q_OBJECT QML_ELEMENT // [...] // [省略部分代码] }QML
有关在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)不同,每当适当时,必须从QML委托中显式调用setData()
方法。这是通过简单地为相应的模型属性分配一个新值来完成的。
注意
edit
角色等同于 Qt::EditRole。有关内置角色名称,请参见 roleNames()。然而,实际生活中的模型通常会注册自定义角色。