using C++ classes and structs in QML
-
You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).
Use
QML_VALUE_TYPE(typeName)
with a lower case name like you currently do.
Then you can addQML_CONSTRUCTIBLE_VALUE
orQML_STRUCTURED_VALUE
(even both).QML_CONSTRUCTIBLE_VALUE
will call a user defined constructorif you have
Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
then doingproperty myStruct myStruct1: 42
will call the constructor with 42 as the foo paramQML_STRUCTURED_VALUE
will assign properties depending on the js object you passed in QML.property myStruct myStruct1: ({myInt: 42})
will create a default constructed MyStruct and assign itsmyInt
property to 42.Note that the type should always be default constructible.
EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.
@GrecKo curiouser and curiouser.
My modified struct:
struct MyStruct { QML_STRUCTURED_VALUE QML_VALUE_TYPE(myStruct) Q_PROPERTY(int myInt MEMBER m_myInt) int m_myInt = 55; MyStruct() {} };
my registration (in main.cpp):
qmlRegisterType<MyStruct>("myStruct", 1, 0, "MyStruct");
and my QML:
property myStruct myStruct1: myStruct ({myInt: 100})
produces a runtime error: "myStruct is not a type."
Isn't using QML_VALUE_TYPE intended to allow me to use "myStruct" in the QML?
-
@GrecKo curiouser and curiouser.
My modified struct:
struct MyStruct { QML_STRUCTURED_VALUE QML_VALUE_TYPE(myStruct) Q_PROPERTY(int myInt MEMBER m_myInt) int m_myInt = 55; MyStruct() {} };
my registration (in main.cpp):
qmlRegisterType<MyStruct>("myStruct", 1, 0, "MyStruct");
and my QML:
property myStruct myStruct1: myStruct ({myInt: 100})
produces a runtime error: "myStruct is not a type."
Isn't using QML_VALUE_TYPE intended to allow me to use "myStruct" in the QML?
As I said : you don't have to use qmlRegisterType. You do have to use Q_GADGET though.
And Q_GADGET adds a
private:
so you need to add backpublic:
struct MyStruct { Q_GADGET QML_VALUE_TYPE(myStruct) QML_STRUCTURED_VALUE Q_PROPERTY(int myInt MEMBER m_myInt) public: int m_myInt = 55; MyStruct() {}; };
and my QML:
property myStruct myStruct1: myStruct ({myInt: 100})
Did I write that? nope. The syntax for structured values is :
property myStruct myStruct1: ({myInt: 100})
Ditch the
myStruct
on the right-hand side. -
As I said : you don't have to use qmlRegisterType. You do have to use Q_GADGET though.
And Q_GADGET adds a
private:
so you need to add backpublic:
struct MyStruct { Q_GADGET QML_VALUE_TYPE(myStruct) QML_STRUCTURED_VALUE Q_PROPERTY(int myInt MEMBER m_myInt) public: int m_myInt = 55; MyStruct() {}; };
and my QML:
property myStruct myStruct1: myStruct ({myInt: 100})
Did I write that? nope. The syntax for structured values is :
property myStruct myStruct1: ({myInt: 100})
Ditch the
myStruct
on the right-hand side.@GrecKo oh, that is a thing of beauty.
Now...about QML_STRUCTURED_VALUE being an undocumented macro, as you said...is it safe to use? I did notice mention of it in a bug report, so maybe it's OK.
Thanks for the help.
EDIT: everything above works, but if possible, I'd like to directly access enums defined in the struct (actually, this was the original point behind this entire thread).
struct MyStruct { Q_GADGET QML_STRUCTURED_VALUE QML_VALUE_TYPE(myStruct) Q_PROPERTY(int myInt MEMBER m_myInt) public: enum MyEnums { Enum0, Enum1, Enum2, Enum3 } m_myEnums; Q_ENUM(MyEnums) int m_myInt = 55; MyStruct() {} };
This attempt doesn't work:
Label { text: "myStruct.Enum3: " + myStruct.Enum3 }
(it doesn't work with "MyStruct" either.)
Can this be made to work? Thanks...
EDIT 2:
I have a workaround, which is to define my enums in a separate class and expose them to QML using the guidelines here, but if possible I'd prefer to avoid this level of indirection, and keep the enums in the struct.
-
@GrecKo oh, that is a thing of beauty.
Now...about QML_STRUCTURED_VALUE being an undocumented macro, as you said...is it safe to use? I did notice mention of it in a bug report, so maybe it's OK.
Thanks for the help.
EDIT: everything above works, but if possible, I'd like to directly access enums defined in the struct (actually, this was the original point behind this entire thread).
struct MyStruct { Q_GADGET QML_STRUCTURED_VALUE QML_VALUE_TYPE(myStruct) Q_PROPERTY(int myInt MEMBER m_myInt) public: enum MyEnums { Enum0, Enum1, Enum2, Enum3 } m_myEnums; Q_ENUM(MyEnums) int m_myInt = 55; MyStruct() {} };
This attempt doesn't work:
Label { text: "myStruct.Enum3: " + myStruct.Enum3 }
(it doesn't work with "MyStruct" either.)
Can this be made to work? Thanks...
EDIT 2:
I have a workaround, which is to define my enums in a separate class and expose them to QML using the guidelines here, but if possible I'd prefer to avoid this level of indirection, and keep the enums in the struct.
-
You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).
Use
QML_VALUE_TYPE(typeName)
with a lower case name like you currently do.
Then you can addQML_CONSTRUCTIBLE_VALUE
orQML_STRUCTURED_VALUE
(even both).QML_CONSTRUCTIBLE_VALUE
will call a user defined constructorif you have
Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
then doingproperty myStruct myStruct1: 42
will call the constructor with 42 as the foo paramQML_STRUCTURED_VALUE
will assign properties depending on the js object you passed in QML.property myStruct myStruct1: ({myInt: 42})
will create a default constructed MyStruct and assign itsmyInt
property to 42.Note that the type should always be default constructible.
EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.
@GrecKo said in using C++ classes and structs in QML:
You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).
Use
QML_VALUE_TYPE(typeName)
with a lower case name like you currently do.
Then you can addQML_CONSTRUCTIBLE_VALUE
orQML_STRUCTURED_VALUE
(even both).QML_CONSTRUCTIBLE_VALUE
will call a user defined constructorif you have
Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
then doingproperty myStruct myStruct1: 42
will call the constructor with 42 as the foo paramQML_STRUCTURED_VALUE
will assign properties depending on the js object you passed in QML.property myStruct myStruct1: ({myInt: 42})
will create a default constructed MyStruct and assign itsmyInt
property to 42.Note that the type should always be default constructible.
EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.
This is amazing !!
Gonna have to explore the possibilities... -
Coming back on what I said:
It is possible to expose an enum from a Q_GADGET.
If you add
pragma ValueTypeBehavior: Addressable
at the top of your QML file you can use the lower case gadget type name in QML and then doproperty int enumValue: myStruct.TestEnum.D
Alternatively you could do it very verbosely with QML_FOREIGN_NAMESPACE like explained here : https://codereview.qt-project.org/c/qt/qtdeclarative/+/510832 (fresh out of the oven)
-
@GrecKo said in using C++ classes and structs in QML:
Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.
namespace scheduleNS { Q_NAMESPACE enum StartAction { START_ACTION_TURN_ON, START_ACTION_TURN_OFF, START_ACTION_BE_READY, START_ACTION_SUSPEND }; Q_ENUM_NS(StartAction) } // namespace
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
-
@GrecKo said in using C++ classes and structs in QML:
Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.
namespace scheduleNS { Q_NAMESPACE enum StartAction { START_ACTION_TURN_ON, START_ACTION_TURN_OFF, START_ACTION_BE_READY, START_ACTION_SUSPEND }; Q_ENUM_NS(StartAction) } // namespace
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
@mzimmers said in using C++ classes and structs in QML:
How do I export a C++ namespace to QML?
I should have asked, is there a newer alternative to the old method of manually registering it like this:
qmlRegisterUncreatableMetaObject(scheduleNS::staticMetaObject, // static meta object "schedule.enums", // import statement 1, 0, // major and minor version of the import "ScheduleNS", // name in QML "Error: only enums"); // error in case someone tries to create a MyNamespace object
This works fine, but given that we no longer need to use qmlRegisterType(), I was wondering whether there was a more modern way of doing the qmlRegisterUncreatableMetaObject() call.
-
@GrecKo said in using C++ classes and structs in QML:
Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.
namespace scheduleNS { Q_NAMESPACE enum StartAction { START_ACTION_TURN_ON, START_ACTION_TURN_OFF, START_ACTION_BE_READY, START_ACTION_SUSPEND }; Q_ENUM_NS(StartAction) } // namespace
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
@mzimmers said in using C++ classes and structs in QML:
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
Have you tried naming the namespace with a capital letter?
As far as I recall only value types are supposed to be lower case. QML is somewhat picky on the namings. -
@mzimmers said in using C++ classes and structs in QML:
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
Have you tried naming the namespace with a capital letter?
As far as I recall only value types are supposed to be lower case. QML is somewhat picky on the namings.@kshegunov I did try that - I still have to register the uncreatable, and add an import statement to any QML wishing to use it.
Still a small price to pay...
-
@GrecKo said in using C++ classes and structs in QML:
Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.
namespace scheduleNS { Q_NAMESPACE enum StartAction { START_ACTION_TURN_ON, START_ACTION_TURN_OFF, START_ACTION_BE_READY, START_ACTION_SUSPEND }; Q_ENUM_NS(StartAction) } // namespace
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
@mzimmers said in using C++ classes and structs in QML:
but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?
With
QML_ELEMENT
orQML_NAMED_ELEMENT
. -
M mzimmers has marked this topic as solved on