Spec-Zone.ru › Qt

Использование 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() и т.д. Дополнительную информацию см. в справочнике по наследованию модели Model subclassing reference.

Полный исходный код этого примера доступен в 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. Пример учебника по чату chat tutorial очень хорошо демонстрирует это, реализуя пользовательскую модель для извлечения контактной информации из базы данных 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.2/qtquick-modelviewsdata-cppmodels.html

Spec-Zone.ru

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