Spec-Zone.ru › Qt 6.1

Использование C++ моделей с Qt Quick Views

Данные, предоставляемые в пользовательской 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:

    QStringList dataList = {
        "Item 1",
        "Item 2",
        "Item 3",
        "Item 4"
    };

    QQuickView view;
    view.setInitialProperties({{ "model", QVariant::fromValue(dataList) }});

Полный исходный код этого примера доступен в examples/quick/models/stringlistmodel в каталоге установки Qt.

Примечание: Нет способа, чтобы представление знало, что содержимое QStringList изменилось. Если QStringList изменяется, необходимо сбросить модель, вызвав QQmlContext::setContextProperty() снова.

Модель на основе 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);

    QList<QObject *> dataList = {
        new DataObject("Item 1", "red"),
        new DataObject("Item 2", "green"),
        new DataObject("Item 3", "blue"),
        new DataObject("Item 4", "yellow")
    };

    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.setInitialProperties({{ "model", QVariant::fromValue(dataList) }});
    ...

QObject* доступен как свойство modelData. Для удобства свойства объекта также доступны непосредственно в контексте делегата. Здесь view.qml ссылается на свойства DataModel в делегате ListView:

ListView {
    width: 100; height: 100
    required model

    delegate: Rectangle {
        required color
        required property string name

        height: 25
        width: 100
        Text { text: parent.name }
    }
}

Обратите внимание на использование свойства color с квалификатором. Свойства объекта не дублируются в объекте model, так как они легко доступны через объект modelData.

Полный исходный код этого примера доступен в examples/quick/models/objectlistmodel в каталоге установки Qt.

Примечание: Нет способа, чтобы представление знало, что содержимое QList изменилось. Если QList изменяется, необходимо сбросить модель, вызвав QQmlContext::setContextProperty() снова.

Подкласс QAbstractItemModel

Модель может быть определена путем наследования от QAbstractItemModel. Это лучший подход, если у вас более сложная модель, которую нельзя поддерживать другими методами. QAbstractItemModel также может автоматически уведомлять QML представление при изменении данных модели.

Роли подкласса QAbstractItemModel могут быть экспонированы в QML путем переопределения QAbstractItemModel::roleNames().

Вот приложение с подклассом QAbstractListModel с именем AnimalModel, которое экспонирует роли type и sizes. Оно переопределяет 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 = 0);
    ...
};

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, который обращается к ролям type и size:

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() и т. д. Подробнее см. в разделе Справочник по наследованию моделей.

Полный исходный код этого примера доступен в examples/quick/models/abstractitemmodel в каталоге установки Qt.

QAbstractItemModel представляет иерархию таблиц, но представления, предоставляемые QML в настоящее время, могут отображать только данные списков. Для отображения дочерних списков иерархической модели используйте тип QML DelegateModel, который предоставляет следующие свойства и функции для использования с моделями списков типа QAbstractItemModel:

  • Свойство роли hasModelChildren для определения, имеет ли узел дочерние узлы.
  • DelegateModel::rootIndex позволяет указать корневой узел.
  • DelegateModel::modelIndex() возвращает QModelIndex, который может быть назначен DelegateModel::rootIndex.
  • DelegateModel::parentModelIndex() возвращает QModelIndex, который может быть назначен DelegateModel::rootIndex.

SQL-модели

Qt предоставляет классы C++, которые поддерживают модели данных SQL. Эти классы работают прозрачно с базовыми 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. Пример учебника по чату demonstrates this very well путем реализации пользовательской модели для извлечения данных контактов из базы данных 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

В приведенных выше примерах используется QQmlContext::setContextProperty() для непосредственного задания значений модели в компоненты QML. Альтернатива заключается в регистрации класса C++ модели как типа QML (либо непосредственно из точки входа C++, или в функции инициализации QML C++ плагина, как показано ниже). Это позволит создавать классы моделей напрямую как типы в QML:

C++
class MyModelPlugin : public QQmlExtensionPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.QmlExtension.MyModel" FILE "mymodel.json")
public:
    void registerTypes(const char *uri)
    {
        qmlRegisterType<MyModel>(uri, 1, 0,
                "MyModel");
    }
}
QML
MyModel {
    id: myModel
    ListElement { someProperty: "some value" }
}
ListView {
    width: 200; height: 250
    model: myModel
    delegate: Text { text: someProperty }
}

См. Написание QML расширений с помощью C++ для получения подробной информации о написании QML C++ плагинов.

Изменение данных модели

Помимо 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() для встроенных имён ролей. Однако реальные модели обычно регистрируют пользовательские роли.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/qtquick-modelviewsdata-cppmodels.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API