У меня есть Q_GADGET MyGadget
определяется в файле mygadget.h
#include <QObject>
class MyGadget {
Q_GADGET
Q_PROPERTY(int value READ value CONSTANT)
public:
MyGadget() = default;
MyGadget(int i)
: _value{i}
{
}
int value() const
{
return _value;
}
private:
int _value{0};
};
Q_DECLARE_METATYPE(MyGadget)
Q_DECLARE_METATYPE(MyGadget*)
и Context
класс, который содержит экземпляр MyGadget
и предоставляет указатель на него в QML через Q_PROPERTY:
#include <QObject>
#include "mygadget.h"
class Context : public QObject
{
Q_OBJECT
Q_PROPERTY(MyGadget* gadget READ gadget CONSTANT)
public:
explicit Context()
: QObject{nullptr}
{
}
MyGadget* gadget() {
return &_gadget;
}
private:
MyGadget _gadget{4};
};
Экземпляр Context
создан в main
и предоставляется QML как свойство контекста:
#include <QGuiApplication>
#include <QQuickView>
#include <QString>
#include <QQmlContext>
#include "context.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
Context c;
// register MyGadget
qmlRegisterUncreatableType<MyGadget>("Test", 1, 0, "MyGadget", "");
qRegisterMetaType<MyGadget*>(); // <- removing this doesn't change anything
// make Context instance a context propery
view.rootContext()->setContextProperty("context", &c);
// show QML
view.setSource(QUrl{QStringLiteral("qrc:/main.qml")});
view.show();
return app.exec();
}
Файл QML, используемый с этим
import QtQuick 2.5
import Test 1.0
Rectangle {
height: 600
width: 800
visible: true
Text {
text: qsTr("Hello World " + context.gadget.value)
anchors.centerIn: parent
}
}
Все компилируется нормально, но при запуске не отображается текст и QML выдает предупреждение
qrc: /main.qml: 9: TypeError: Невозможно прочитать свойство ‘value’ из null.
Если я удалю звонок qmlRegisterUncreatableType<MyGadget>
в main
и соответствующий import Test 1.0
в файле QML вместо этого отображается текст «Hello World undefined».
Единственный способ напечатать «Hello World 4», как и ожидалось, — это иметь Context::gadget
вернуть копию сохраненного MyGadget
объект вместо указателя на него, или сделать MyGadget
Q_OBECT
вместо. Но оба они не являются жизнеспособными вариантами в моем реальном приложении, так как здесь мне нужна ссылочная семантика, но в других местах я также хотел бы иметь семантику значений для класса, соответствующего MyGadget
в этом примере.
Как я могу получить QML для чтения правильного значения свойства?
Гаджеты принципиально ограничены дизайном, вы не можете работать с ними по указателю из QML, вы должны работать с экземплярами, что также подразумевает передачу и возврат по значению. Это также означает, что любые ваши манипуляции применяются к копии, а не к реальному объекту, который вы намеревались.
Есть способ обойти это, используя PIMPL, и, по сути, объект гаджета должен быть указателем, то есть он имеет только одну переменную-член, которая является указателем на фактическую реализацию данных объекта, которая является отдельным объектом.
Это делает копирование объекта гаджета очень дешевым, поскольку это всего лишь указатель, и все копии ссылаются на одни и те же данные объекта, поэтому ваши изменения не будут потеряны.
Обновить:
В любом случае, если ваше свойство гаджета является просто int или каким-либо другим примитивом, как указано в вашем комментарии, вместо этого используйте его как свойство int. У вас могут быть свойства с уведомлениями, QML будет жаловаться на то, что они не имеют уведомлений, если вы используете их в привязках, но вы можете просто передать один фиктивный сигнал, который никогда не излучается, и вы настроены.
И даже если вы выбираете PIMPL, нет необходимости выделять его динамически на основе примитивов или вообще. Указатель не заботится о том, на что он указывает, кроме случаев, когда существует опасность, что память станет недействительной, а указатель — зависший.
Других решений пока нет …