Qt QML Hot Tips #8
Mike Trahearn
Qt Professional Services | Qt Academy | Qt Lifetime Champion?????? | Director Codecept Software Pty Ltd | Qt QML C++ Specialist | Unique Thinker, Detailed Craftsman with Precise Foresight and a Personal Approach
Part 8 - How to save face by using namespaces - a quick guide to QML and enums!
Following a rather extended period since Qt QML Hot Tips #7 (handling big resources) I really felt it compelling to share the following new article about enumerations and some clever ways you can use them in QML.
Enumerations are very useful and allow for some very nice auto-completing code in QML. A very simple case would be as follows:
// MyObject.h
#include <QObject>
class MyObject : public QObject {
Q_OBJECT
QML_ELEMENT
public:
explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}
enum MyEnum {
Value1,
Value2 // etc
};
Q_ENUM(MyEnum);
};
And in QML you would be able to write the following:
import MyModule // assuming appropriate qt_add_qml_module
MyObject {
property int testValue: MyObject.Value1 // use enum via MyObject
}
Getting your ENUMS in a twist?
However you may get yourself into an odd situation when you need to use an enumeration in property that happens to be defined in a different class. We might write something like this:
// AnotherObject.h
#include <QObject>
#include "MyObject.h"
class AnotherObject: public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(MyObject::MyEnum value READ value .... FINAL)
// details omitted for brevity
}
And in QML you would have to write the following (which works fine):
import MyModule // assuming appropriate qt_add_qml_module
AnotherObject {
value: MyObject.Value1 // still need to use enums via MyObject!
}
We can see that we have had to compromise in our "niceness" because we have had to "borrow" an enumeration from a different class and so much is obvious from the QML. What would be "nicer" is if we could not have to borrow in QML but fix it in the C++ so the implementation is hidden from the QML developer.
Solution
To solve for this we introduce the following macros:
Q_NAMESPACE
Q_ENUM_NS
QML_EXTENDED_NAMESPACE
We start by restructuring our enumeration as follows:
领英推荐
// MyEnums.h
// a header used JUST for defining an enumeration
// that we want to include in a number of places.
#include <QObject>
// define a namespace in which we shall declare an enumeration
namespace MyNamespace {
Q_NAMESPACE // add Q_NAMESPACE
// no need for Q_OBJECT, Q_GADGET, QML_ELEMENT here!
enum MyEnum {
Value1,
Value2 // etc
};
Q_ENUM_NS(MyEnum); // use Q_ENUM_NS instead of Q_ENUM
}
To use the MyEnum enumeration in another class it is now quite simple:
// AnotherObject.h
#include <QObject>
#include "MyEnums.h"
class AnotherObject: public QObject {
Q_OBJECT
QML_ELEMENT
// qualify the namespace in the property type!
Q_PROPERTY(MyNamespace::MyEnum value READ value .... FINAL)
// implementation details omitted for brevity
}
While this pattern can be used for any number of classes now, as it stands, the?restructured MyEnum enumerations can’t be used from QML just yet...
import MyModule
AnotherObject {
value: // what goes here???
}
We need to make the following modification to our "AnotherClass" by "extending it" by the MyNamespace such that the class now brings with it the MyEnum enumerations as well - and it now "belongs" to it.
// AnotherObject.h
#include <QObject>
#include "MyEnums.h"
class AnotherObject: public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(MyNamespace::MyEnum value READ value .... FINAL)
// extend this class with the namespace that holds the enums
QML_EXTENDED_NAMESPACE(MyNamespace)
// implementation details omitted for brevity
}
And finally we can do:
import MyModule
AnotherObject {
value: AnotherObject.Level1 // enums accessed as if they belong.
}
Advanced Case
The above may sound a little contrived for the simple case described. However this article is borne out of the need to provide attached objects with enumerations that are shared amongst two or more classes.
AttachingObject: declares QML_ATTACHED(AttachedObject) and both have a property which shares the same enumeration type. In this case we include the enumeration header with its namespace and extend the AttachedObject with the namespace so that the enumeration values are available in QML when prefixed with the AttachedObject qualification.
Summary
We considered the problem of "borrowing" a common enumeration into different classes and consequently different QML usages for potentially the same enumeration. While the default case is still valid and you may want to use it that way, there are times where we want to "change the owner" of an enumeration by extending a dedicated "other" class with a namespace containing the enumeration definitions in order to make the QML more consistent and readable.