This is another one of those “it’s all in the documentation”, but for whatever reason, I had a heck of a time finding the info when I needed it. (The Qt documentation is generally great — I’m not beefing about that, but rather my personal inability to remember bits of it and not have enough remembered that I can search on things efficiently).
There’s times where you want to have something in C++ call a method defined by a QML item. In my case, I had a ListView
that I wanted to manually reposition when its model (a C++ object derived from QStandardItemModel
) changed under certain circumstances. I began with a ListView
that looked something like this:
ListView { id: events objectName: "events" property bool moved: false model: appmodel delegate: MyItemDelegate {} Component.onCompleted: positionViewAtIndex(count - 1, ListView.End) onMovementStarted: moved = true function positionListAtEnd() { if (!moved) events.positionViewAtIndex(events.count - 1, ListView.End) } }
Here, I’m going to use the moved
property to track whether the user’s interacted with the list view at all; if she’s moved the list view, I don’t want to start yanking it around when the model updates, but if it’s just sat unused and the model updates, I want the list to always show the last item in the model. When the list view first loads, I do that manually using the onCompleted
signal handler; I also want to do it when the model changes, by having the code that updates the model invoke the ListView
‘s positionListAtEnd
method.
The trick is actually getting a reference to the QObject
that corresponds to my ListView
. In QML, items are referenced by their id
property; in Qt’s meta-object system, they’re referenced by object name—denoted by the objectName
property, a string bearing the object’s name.
Don’t confuse the two! You can’t link QML things using object names, and, as near as I can tell, the Qt meta-object system’s C++ implementation doesn’t care one whit about QML ids.
So I give my ListView
an objectName
property, which for sanity’s sake is the same as the id
I’ve assigned: "events"
. It’s a string, not a name, so those quotes in the QML are important. Once I’ve done that, I can find this object by name in my QML hierarchy using the following bit of C++:
QObject *object = mView.rootObject(); if (!object) return; QObject* listView = object->findChild<QObject *>("events"); QVariant returnedValue; QMetaObject::invokeMethod(listView, "positionListAtEnd", Q_RETURN_ARG(QVariant, returnedValue));
The mView
field is just the QDeclarativeView
that renders my QML; you can see how I originally linked my C++ and QML in my previous post, Adding C++ Objects to your QML. It has a root object, a QObject
descendant (really, a QGraphicsObject
that corresponds to the top level of the QML object hierarchy being displayed). To invoke my QML method, I use Qt’s meta-object protocol to first find the child named "events"
and then invoke its positionListAtEnd
method, discarding the value it returns.
There’s no magic here—as long as you remember that QML items can have an objectName
property, and that property is used internally by Qt’s object hierarchy as the object’s name.