Qt QML Hot Tips #4
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 4 - May I See Your ID?
In this article I cover one of the easiest parts of the QML syntax - but its inclusion in this series mean, yep, I've seen some right clangers. You'd be surprised how "easy" it seems to be to go wrong with such a simple thing - because actually, it isn't.
Unfortunately for me, I never get asked for my ID when I visit certain stores for celebratory purposes (which I might do today being a significant career anniversary), since, well I ahem don't look young enough to be asked (sigh).
However, ID is very important in the language of QML and it is the primary way you figure out who someone (or rather property) belongs to. In C++ we have pointers and references, but in QML we have the id attribute. The id attribute is not a property but a special case where we give our QML object ad id by which we can reference it elsewhere.
Users of QWidgets may be all too familiar with the objectName string property which is of course available on all QML objects because it comes for free from QObject. You can easily set this in QML and is a great aid to debugging and also for tools like Squish to locate named objects if it can't get the id.
What's the big IDea?
Here's a contrived example which quickly demonstrates the difference between id and objectName:
import QtQuick // I'm assuming Qt 5.15+
QtObject {
id: myObject // this is the id
objectName: "myObject" // this is the objectName
// now we can use the id to reference a property
Component.onCompleted: console.log(myObject.objectName)
}
Does everything need an id?
Yes. Mostly. Normally. It is definitely something you want to do. I mentioned this in an earlier article and we'll cover that very briefly here. If you create an instance of an object you want to reference then you'll probably want to give it an id. Reference? Like the above simple case, but we're also talking about making bindings to other objects or using properties of them in JavaScript functions.
But there are some notable exceptions
When creating a component with the Component um, component... you give the Component the id rather than the component that it is wrapping. That's a mouthful, but here's the gist:
Component
id: myCustomObject
MyCustomObject {
// no id here
// add your properties here
}
}
Loader {
sourceComponent: myCustomObject
}
Note that properties on an object so declared above will only evaluate their bindings once the component is actually created as an object. Without being created, the component is only parsed and understood by the QQmlEngine, but not created. If you do give it an id to the component (e.g. in this case MyCustomObject), then you might be tempted to create bindings TO it, which won't work because the object in the lines of QML where it is declared are NOT an object instance, but the instructions that one may be created FROM - and hence we call it a component, not an object (element).
You shouldn't really be trying to access any property belonging to a model delegate from outside that delegate because it is a really bad design pattern straight off, but importantly its lifetime is not guaranteed, not just because the model may change, but certain views e.g. ListView even recycle delegates for a really nice performance optimisation (which when I first saw it was one of those "hats off to whoever wrote that - Qt is awesome!" moments . If you need access to model data at a specific index then you should be looking at providing some Q_INVOKABLE methods on your model to do so. And be asking yourself why.
This one is subtle. If like me you've spend a lot of time digging into Qt Quick Controls 2 and creating your own style suites, you'll have very quickly come across things like "background" and "contentItem". These are the visual implementations of the behaviour described by the template.
The QQmlEngine will create an instance of every component with an id.
领英推荐
The above statement is important when it comes to delegates within a styled component because you may well want to provide customisations to your new fancy component and consequently override for example the background delegate. In such a case, if your base style implementation delegate has an id, QML will create BOTH that one AND the the overriding one - which is a waste of time. If the style delegates don't have ids, they won't get created if the delegate property is overridden.
You may wish for some reason to encapsulate and hide away some functions, bindings or other processing inside another component and it for whatever reason doesn't need an id because it is not referenced directly anywhere.
Don't you dare call me that...
There are some rules about ids:
1) can't have spaces or symbols
2) must start with a lower case letter.
3) they can't be any of the QML reserved keywords
4) must be unique within the QML file (which is called the context see below)
5) must be accessible within the context - don't you even think about referencing an object by its id if that object is declared outside of your immediate QML file (context). This "used to be allowed" in Qt 5 but no more. Such cases are called unqualified ids are now considered erroneous and without going into further detail, not only will the QML tooling shout at you but Ulf Hermann will personally shoot you with his "foot guns".
Bonus: Can you get the id from c++? Yes you can!
To get an object's id from C++ you must first get the QQmlContext that the object was created in:
QQmlContext *context = qmlContext(object);
If the object implements QQmlParserStatus, then the QQmlContext is available during or after componentComplete() but not before.
QString id = context->nameForObject(object);
You can also get the objectName too. Note that the objectName can also be set from c++ in which case it can be gotten soon after the object constructor. If the objectName is set from QML, then only after componentCompleted() occurs.
QString id = object->objectName();
For More Reading
And some more examples of these and potential solutions coming up in the next article...
Senior Software Engineer
3 个月Great points! > The QQmlEngine will create an instance of every component with an id. This will be the case if the style components have a nested component with an id as well. ``` Button { ???id: root ???background: Rectangle { ???????color: "red" ???????Item { ???????????id: it ???????} ???} } ```