警告

本节包含自动从C++转换为Python的片段,可能包含错误。

使用C++模型与Qt Quick视图#

使用在C++中定义的Qt Quick视图

自定义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<QObject*>通过将列表中对象的属性作为角色提供

以下应用程序创建了一个带有可访问的Q_PROPERTY值的DataObject类,这些属性在将QList<DataObject*>暴露给QML时可以作为名为角色的属性访问

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.qmlListView代理人中引用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::roleNames() 来将 QAbstractItemModel 子类的角色暴露给 QML。

这是一个使用了名为 AnimalModel 的 QAbstractListModel 子类的应用程序,它公开了 类型大小 角色。它重新实现了 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)}})            ...

此模型通过一个 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() 返回一个 QModelIndex,可以分配给 DelegateModel::rootIndex。

  • DelegateModel::parentModelIndex() 返回一个 QModelIndex,可以分配给 DelegateModel::rootIndex。

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() 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() 以将 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()。

根据模型使用的编辑策略,更改要么排队稍后提交,要么立即提交。

您还可以通过调用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()信号。

与QListView或QTableView之类的C++项视图不同,必须在适当的时候从QML代理中显式调用setData()方法。这可以通过将新值赋给相应的模型属性来完成。

edit角色的内容与Qt::EditRole相同。有关内置角色名称的详细信息,请参阅roleNames()。然而,实际生活中的模型通常注册自定义的角色。