Застосування Q_GADGET в C++&QtQuick

Дивлюся на форумах рунета, люди починають писати на C++&Qt Quick і використовують спадкоємців від QObject, для так званих типів значень(Value Type). Мартін Фаулер їх називає Value Object. Хоча є макрос Q_GADGET дозволяє використовувати QMetaObject з деякими обмеженнями, але без успадкування від 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, на одному екрані виводиш модель з мінімум інформацією, а на наступному екрані більш докладно:


На мою особисту думку, такі value type дуже зручні.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.