Применение Q_GADGET в C++&QtQuick

в 8:33, , рубрики: qt, QtQuick, Программирование

Смотрю на форумах рунета, люди начинают писать на C++&Qt Quick и используют наследников от QObject, для так называемых типов значений(Value Type). Мартин Фаулер их называет Value Object. Хотя есть макрос Q_GADGET позволяющий использовать QMetaObject c некоторыми ограничениями, но без наследования от QObject. Все что будет описано ниже результат экспериментов с Qt Quick. Буду рад узнать что-то новое из комментариев.

Пример таких типов QPoint, QGeoCoordinate и т.д. Наследоваться от QObject и использовать макрос Q_OBJECT неудобно для таких типов:

  • QObject защищен от копирования;
  • нужно возвращать значение по указателю. Приходится задумываться о CppOwnership/JavaScriptOwnership из перечисления QQmlEngine::ObjectOwnership.

Q_GADGET позволяет нам использовать:

  • Q_ENUM;
  • Q_PROPERTY;
  • Q_INVOKABLE.

Ограничение:

  • Отсутствие поддержки сигналов и слотов.

Если наше приложение просто отображает то, что пришло с сервера, то можно завести структуру:

struct PlayItem
{
private:
    Q_GADGET
    Q_PROPERTY(int episode MEMBER episode)
    Q_PROPERTY(QString mp4Url MEMBER mp4Url)
    Q_PROPERTY(QString name MEMBER name)

public:

    int episode;
    QString mp4Url;
    QString name;

    static PlayItem fromJson(const QJsonObject& jobj);

};

Q_DECLARE_METATYPE(PlayItem)

Q_DECLARE_METATYPE здесь используется для регистрации типа в QVariant. Зачем он тут нужен, об этом будет позже.

Такие типы можно использовать в свойствах других объектов:

class Size
{
    Q_GADGET

public:

    Q_INVOKABLE quint16 rows() const noexcept;
    Q_INVOKABLE quint16 column() const noexcept;
    Q_INVOKABLE bool isNull() const noexcept;

//..
};

class Crossword: public QObject
{
    Q_OBJECT
    Q_PROPERTY(Size size READ size)

public:

    Crossword(QObject* parent = nullptr);

    Size size() const noexcept;
}

И спокойно работаем в js:

var csize = crossword.size;
//...
rows = csize.rows();
column = csize.column();

Q_GADGET и Q_INVOKABLE

Почему то мы не можем использовать ValueType в методах помеченными Q_INVOKABLE. За то можно возвращать QVariant с ValueType! И так же использовать его в js! Это очень удобно в моделях, заместо множества ролей и switch:

QVariant BucketModel::data(const QModelIndex &index, int role) const
{
    switch (role)
    {
        case Bucket:
            return QVariant::fromValue(m_buckets[index.row()]);
        default:
            return QVariant();
    }

}

QHash<int, QByteArray> BucketModel::roleNames() const
{
    static const QHash<int, QByteArray> roles = {
        {Bucket, "bucket" }
    };

    return roles;
};

В делегате как обычно:

delegate: ItemDelegate {
    width: parent.width
    text: bucket.name

    Image{
        visible: bucket.id === b2App.settings.bucketId

        anchors{
            right:parent.right
            verticalCenter: parent.verticalCenter
            margins: 8
        }

        source: "qrc:/icons/tick/tick.png"
    }

Item и property

Такие типы можно использовать как свойства и делать на них привязки. Это осуществляется через общий тип(generic type):

Item {
    property var film

    //...
    Label {
        text: film.year
        //...
    }

    Label {
        text: film.countries
        //...
    }
  //...
}

Так как до инстанцирования тип неизвестен, то во время выполнения ругается(но не падает): TypeError: Cannot read property 'year' of undefined.

Убрать эту ругань можно инициализировав свойство, каким-нибудь экземпляром:

QQmlApplicationEngine engine;

Film film;
engine.rootContext()->setContextProperty("emptyFilm", QVariant::fromValue(film));

Item {
    property var film: emptyFilm

    //...
    Label {
        text: film.year
        //...
    }

    Label {
        text: film.countries
        //...
    }
 //...
}

Это оказывается очень удобно, когда используется StackView, на одном экране выводишь модель с минимум информацией, а на следующем экране более подробно:

Применение Q_GADGET в C++&QtQuick - 1

Применение Q_GADGET в C++&QtQuick - 2

По-моему личному мнению, такие value type очень удобные.

Автор: RPG18

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js