Spec-Zone.ru › Qt 5.9

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

Данные, предоставляемые в пользовательской C++ модели

Модели могут быть определены на C++ и затем предоставлены для QML. Это полезно для экспонирования существующих C++ моделей данных или сложных наборов данных в QML.

Класс модели на C++ может быть определен как QStringList, QVariantList, QObjectList или QAbstractItemModel. Первые три полезны для экспонирования более простых наборов данных, в то время как QAbstractItemModel обеспечивает более гибкое решение для более сложных моделей.

Для видеоурока, который проведет вас через весь процесс экспонирования C++ модели в QML, см. Урок по использованию C++ моделей в QML.

Модель на основе QStringList

Модель может быть простой QStringList, которая предоставляет содержимое списка через роль modelData.

Вот ListView с делегатом, который ссылается на значение элемента модели с помощью роли modelData:

ListView {
    width: 100; height: 100

    model: myModel
    delegate: Rectangle {
        height: 25
        width: 100
        Text { text: modelData }
    }
}

Приложение Qt может загрузить этот документ QML и установить значение myModel в QStringList:

    QStringList dataList;
    dataList.append("Item 1");
    dataList.append("Item 2");
    dataList.append("Item 3");
    dataList.append("Item 4");

    QQuickView view;
    QQmlContext *ctxt = view.rootContext();
    ctxt->setContextProperty("myModel", 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;
    dataList.append(new DataObject("Item 1", "red"));
    dataList.append(new DataObject("Item 2", "green"));
    dataList.append(new DataObject("Item 3", "blue"));
    dataList.append(new DataObject("Item 4", "yellow"));

    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    QQmlContext *ctxt = view.rootContext();
    ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
    ...

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

ListView {
    width: 100; height: 100

    model: myModel
    delegate: Rectangle {
        height: 25
        width: 100
        color: model.modelData.color
        Text { text: 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);
    QQmlContext *ctxt = view.rootContext();
    ctxt->setContextProperty("myModel", &model);
    ...

Эта модель отображается делегатом ListView, который обращается к ролям type и size:

ListView {
    width: 200; height: 250

    model: myModel
    delegate: Text { 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. Пример чата показывает это очень хорошо, реализуя пользовательскую модель для извлечения контактных данных из базы данных SQLite.

Редактируемая модель данных

Помимо roleNames() и data(), редактируемые модели должны переопределить метод setData для сохранения изменений в существующих данных SQL. Следующая версия метода проверяет, является ли данный индекс модели допустимым, и равно ли role Qt::EditRole, перед вызовом родительской версии:

bool SqlEditableModel::setData(const QModelIndex &item, const QVariant &value, int role)
{
    if (item.isValid() && role == Qt::EditRole) {
        QSqlTableModel::setData(item, value,role);
        emit dataChanged(item, item);
        return true;
    }
    return false;

}

Примечание: важно испустить сигнал dataChanged() после сохранения изменений.

В отличие от C++ представлений элементов, таких как QListView или QTableView, метод setData() должен быть явно вызван из QML при необходимости. Например, при сигнале editingFinished() или accepted() TextField. В зависимости от используемой модели 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++ плагинов.

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

Spec-Zone.ru

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