QML and Qt Quick
-
Hello Everyone,
I have a clarification question about context properties and their usage.Defining a context property in C++ and assigning it to a QML property, such as
// c++
QQmlApplicationEngine engine;
MyCppClass myObject;
engine.rootContext()->setContextProperty("myObject", &myObject);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));// qml
Rectangle {
width: 100
height: 100
property QtObject m_myObject: myObject
Text {
text: m_myObject.someProperty
}
}By assigning the context property (myObject) to a QML property (m_myObject), we create a duplicate reference to the same object in QML. This doesn’t duplicate the object itself but creates another reference to it. It can be helpful if this reference is used to simplify access or create a more readable QML structure.
However, if the duplicate property doesn't add value, I believe it introduces unnecessary overhead. What is the best practice for this scenario? What issues can we face by having similar code across whole source files? Can we have performance issues because of this? -
Please edit this post and format the code using the </> code tags.
Think about the time of those offering support in their free time: Nobody wants to decipher unformatted code. -
@hnar The best practice is not using context properties https://doc.qt.io/qt-6/qtqml-cppintegration-overview.html
-
@GrecKo said in QML and Qt Quick:
@hnar The best practice is not using context properties https://doc.qt.io/qt-6/qtqml-cppintegration-overview.html
Hmm, I started switching to use
qmlRegisterSingletonInstance
recently in accordance with some previous advice on this forum. It now seems like that is being somewhat deprecated too. -
@Bob64 said in QML and Qt Quick:
@GrecKo said in QML and Qt Quick:
@hnar The best practice is not using context properties https://doc.qt.io/qt-6/qtqml-cppintegration-overview.html
Hmm, I started switching to use
qmlRegisterSingletonInstance
recently in accordance with some previous advice on this forum. It now seems like that is being somewhat deprecated too.Indeed,
QML_ELEMENT
+QML_SINGLETON
is the way to go -
@JKSH said in QML and Qt Quick:
Indeed,
QML_ELEMENT
+QML_SINGLETON
is the way to goYes, but I think
QML_FOREIGN
is also needed, and it'sQML_NAMED_ELEMENT
rather thanQML_ELEMENT
from what I understand from the 6.7 doc that was linked. I am still on 5.15 and this stuff is not yet documented there in the same way.To be clear, the use case being addressed is the ability to register an already constructed object as a QML singleton, rather than it actually being a C++ singleton. This is important if one wants to manage dependencies on the C++ side. In my experience, it is rare that an API class being exposed to QML has a completely standalone implementation that has no dependencies on external classes, and I want to control how those dependencies are passed in at construction rather than having to introduce a cascade of singletons.
-
here's how I did it with 6.7. (with some help of @GrecKo)
https://t1p.de/ekkeQML_SINGLETON -
@ekkescorner Thank you! I have bookmarked your site - it looks like you have a lot of useful information there!
-
@Bob64 said in QML and Qt Quick:
Yes, but I think
QML_FOREIGN
is also needed, and it'sQML_NAMED_ELEMENT
rather thanQML_ELEMENT
from what I understand from the 6.7 doc that was linked. I am still on 5.15 and this stuff is not yet documented there in the same way.QML_ELEMENT
uses the C++ class name as the QML type nameQML_NAMED_ELEMENT()
ignores the C++ class name and lets you specify a different name for the QML type
Aside from this difference, these 2 macros do the same thing.
QML_FOREIGN
is only needed for cases where your original class doesn't haveQML_ELEMENT
/QML_NAMED_ELEMENT()
(for example, if the original class is from a 3rd-party library that you don't control). You would then need to create a dummy/wrapper/boilerplate class to expose the original (foreign) class to QML.To be clear, the use case being addressed is the ability to register an already constructed object as a QML singleton, rather than it actually being a C++ singleton. This is important if one wants to manage dependencies on the C++ side. In my experience, it is rare that an API class being exposed to QML has a completely standalone implementation that has no dependencies on external classes, and I want to control how those dependencies are passed in at construction rather than having to introduce a cascade of singletons.
You can certainly do this via
QML_ELEMENT
/QML_NAMED_ELEMENT()
+QML_SINGLETON
:- Construct your instance before loading your QML code
- Store your desired instance in a global/static variable,
myInstance
- Implement a static method with this signature:
static MyClass* MyClass::create(QQmlEngine*, QJSEngine*)
-- this method should callQJSEngine::setObjectOwnership(myInstance, QJSEngine::CppOwnership); and then return
myInstance` - Load your QML code
When the QML engine loads your module, it will call
MyClass::create()
and use the returned pointer as the singleton object.See the
MyNonDefaultConstructibleSingleton
example at https://doc.qt.io/qt-6/qml-singleton.html#registering-a-class-to-provide-singletonsAddendum: "Why go through all this trouble?"
In case you're wondering, theqmlRegister*()
functions perform registration at runtime, whileQML_ELEMENT
and friends perform registration at compile-time. Compile-time registration enables the Qt Quick Compiler to optimize your code: https://www.qt.io/blog/qt-6.6-and-6.7-make-qml-faster-than-ever-a-new-benchmark-and-analysis -
@JKSH said in QML and Qt Quick:
You can certainly do this via QML_ELEMENT/QML_NAMED_ELEMENT() + QML_SINGLETON:
- Construct your instance before loading your QML code
- Store your desired instance in a global/static variable, myInstance
- Implement a static method with this signature: static MyClass* MyClass::create(QQmlEngine*, QJSEngine*) -- this method should call QJSEngine::setObjectOwnership(myInstance, QJSEngine::CppOwnership); and then return myInstance`
- Load your QML code
This also requires that MyClass is not defaut constructible (= has no constructor without parameter or with only default parameter).
This doesn't work if there is aMyClass()
constructor orMyClass(QObject* parent = nullptr)
. This is an infortunate requirement and that's why using another QML_FOREIGN class to declare the singleton is sometime recommended. -