Spec-Zone.ru › Qt 5.6

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

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

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

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

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

Модель на основе 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/archives/qt-5.6/qtquick-modelviewsdata-cppmodels.html

Spec-Zone.ru

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