I look at the Runet forums, people start writing in C ++ & Qt Quick and use the heirs from QObject , for the so-called value types (Value Type). Martin Fowler calls them Value Object . Although there is a Q_GADGET macro that allows you to use QMetaObject with some restrictions, but without inheritance from QObject. All that will be described below is the result of experiments with Qt Quick. I will be glad to learn something new from the comments.
An example of such types is QPoint, QGeoCoordinate, etc. Inheriting from QObject and using the Q_OBJECT macro is inconvenient for these types:
Q_GADGET allows us to use:
Limit:
If our application simply displays what came from the server, then we can create a structure:
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 here is used to register a type with QVariant . Why is he needed here, this will be later.
Such types can be used in the properties of other objects:
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; }
And we work quietly in js:
var csize = crossword.size; //... rows = csize.rows(); column = csize.column();
For some reason we cannot use ValueType in methods marked Q_INVOKABLE . For that, you can return QVariant with ValueType! And also use it in js! This is very convenient in models, instead of the set of roles and 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; };
In the delegate as usual:
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" }
Such types can be used as properties and bind to them. This is done through the generic type:
Item { property var film //... Label { text: film.year //... } Label { text: film.countries //... } //... }
Since the type is unknown prior to instantiation, it swears (but does not crash) at TypeError: Cannot read property 'year' of undefined
.
You can remove this abuse by initializing the property with some kind of instance:
QQmlApplicationEngine engine; Film film; engine.rootContext()->setContextProperty("emptyFilm", QVariant::fromValue(film));
Item { property var film: emptyFilm //... Label { text: film.year //... } Label { text: film.countries //... } //... }
This is very convenient when using StackView , on one screen you display a model with minimum information, and on the next screen in more detail:
In my personal opinion, such value type is very convenient.
Source: https://habr.com/ru/post/307816/
All Articles