Unsolved QObject::findChild() - "simple" case not working
-
Trying to find a qml item by name from C++ is failing - I have done this in the past, I cannot figure out why this "simple" case doesn't work.
*main.qml contains an item with objectName: "topographySeries"
*main.cpp creates QQmlApplicationEngine which loads main.qml
*main.cpp code then invokes QObject::findChild("topographySeries"), which returns nullptr for some reasonmain.qml snippet:
import QtQuick 2.0 import QtQuick.Window 2.12 import QtDataVisualization 1.2 Window { visible: true id: mainWindow width: 640 height: 480 title: qsTr("TopoSeries test") Item { width: 640 height: 480 Surface3D { width: parent.width height: parent.height Surface3DSeries { id: topographySeries; objectName: "topographySeries"
And here is the main.cpp snippet:
g_appEngine = new QQmlApplicationEngine(); const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(g_appEngine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); g_appEngine->load(url); if (g_appEngine->rootObjects().isEmpty()) { qDebug("Empty rootObjects"); return -1; } // Get root window pointer g_rootWindow = qobject_cast<QQuickWindow*>(g_appEngine->rootObjects().value(0)); QObject *object = g_rootWindow->findChild<QObject *>("topographySeries"); if (!object) { qDebug("Couldn't find topographySeries object"); } else { qDebug("Found topographySeries object"); }
qml items are created and displayed, but the C++ code cannot find the "topographySeries" object - and I cannot figure out why. What am I doing wrong?
Thanks
Tom -
@tom-asso topographySeries != topographicSeries
objectName: "topographySeries" ... QObject *object = g_rootWindow->findChild<QObject *>("topographicSeries");
-
@jsulm - thanks very much! That was a clumsy typo in my post, but using the correct name in my actual code still does not find the Surface3DSeries object. Another test - following is a qml that creates several items: Window, Item, Surface3D, Surface3DSeries, and Button - each has a unique objectName. Of these, my C++ code does NOT find the Window and Surface3DSeries objects. Why is that?
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.3 import QtDataVisualization 1.2 Window { objectName: "myWindow" visible: true width: 640 height: 480 id: mainView Item { objectName: "myItem" width: parent.width height: parent.height Surface3D { objectName: "mySurface" width: parent.width height: parent.height Surface3DSeries { objectName: "mySurfaceSeries" ItemModelSurfaceDataProxy { itemModel: dataModel rowRole: "longitude" columnRole: "latitude" yPosRole: "elevation" } } } ListModel { id: dataModel ListElement{ longitude: "20"; latitude: "10"; elevation: "1000"; } ListElement{ longitude: "21"; latitude: "10"; elevation: "1200"; } ListElement{ longitude: "22"; latitude: "10"; elevation: "900"; } ListElement{ longitude: "23"; latitude: "10"; elevation: "1100"; } ListElement{ longitude: "20"; latitude: "11"; elevation: "1000"; } ListElement{ longitude: "21"; latitude: "11"; elevation: "1300"; } ListElement{ longitude: "22"; latitude: "11"; elevation: "1250"; } ListElement{ longitude: "23"; latitude: "11"; elevation: "1200"; } } Button { objectName: "myButton" text: qsTr("my button") } } }
Here is main.cpp snippet, which tries to find each of the object names:
QObject *root = qobject_cast<QObject *>(engine.rootObjects().value(0)); char *objectName = "myWindow"; findObject(objectName, root); objectName = "myItem"; findObject(objectName, root); objectName = "mySurface"; findObject(objectName, root); objectName = "mySurfaceSeries"; findObject(objectName, root); objectName = "myButton"; findObject(objectName, root); return app.exec(); } QObject *findObject(char *name, QObject *root) { QObject *object = root->findChild<QObject *>(name); if (!object) { qDebug() << "Couldn't find object " << name; } else { qDebug() << "Found object " << name; } return object; }
Here's the program output:
Couldn't find object myWindow Found object myItem Found object mySurface Couldn't find object mySurfaceSeries Found object myButton
Again, thanks for your help.
-
@jsulm - I thought my qml might have some weird problem, but the qmlsurface example provided with Qt seems to have the same issue; C++ code in main.cpp cannot find a Surface3DSeries in main.qml. I slightly modified the example's main.qml, adding objectName "heightSeries" and objectName "surfaceSeries" to the two Surface3DSeries:
Surface3DSeries { id: surfaceSeries objectName: "surfaceSeries" flatShadingEnabled: false drawMode: Surface3DSeries.DrawSurface ItemModelSurfaceDataProxy { //! [5] //! [6] itemModel: surfaceData.model rowRole: "longitude" columnRole: "latitude" yPosRole: "height" } //! [6] onDrawModeChanged: checkState() } //! [4] Surface3DSeries { id: heightSeries objectName: "heightSeries" flatShadingEnabled: false drawMode: Surface3DSeries.DrawSurface visible: false
I slightly modified the example's main.cpp to search for objects named "heightView" and "surfaceView":
viewer.show(); QObject *root = viewer.rootObject(); QObject *object = root->findChild<QObject *>("surfaceSeries"); if (!object) { qDebug() << "Could not find surfaceSeries"; } else { qDebug() << "Found surfaceSeries"; } object = root->findChild<QObject *>("heightSeries"); if (!object) { qDebug() << "Could not find heightSeries"; } else { qDebug() << "Found heightSeries"; } return app.exec();
And the above code cannot find "surfaceSeries" or "heightSeries". I must have some fundamental misunderstanding of how the Qt object hierarchy works... :-(
-
@tom-asso I'm not sure, but maybe you're searching for the object too early? I mean, if QML loading is asynchronous and you try to search just after load() the QML is maybe not yet completely loaded? Try to use a timer and search after one second or so just to check.
-
@jsulm - added sleep(2) right after engine.load(url) in main.cpp - does not alter the result...
Anyone able to reproduce my results?
Thanks! -
@tom-asso
When @jsulm says "if QML loading is asynchronous", if that happens in the same process (I assume it does, I don't use QML) yoursleep(2)
won't help! If you wish to check the issue he suggests I think you do need to set up aQTimer
.Or better, if I understand right, there is signal https://doc.qt.io/qt-5/qqmlapplicationengine.html#objectCreated for when loading is completed? Ah, I see you have a slot for this, you could try your search from there.
-
@jonb said in QObject::findChild() - "simple" case not working:
https://doc.qt.io/qt-5/qqmlapplicationengine.html#objectCreated
@jonB - thanks! I set up a QTimer as advised, but the C++ still does not find objects for Window or Surface3DSeries after several seconds. Both the Window and Surface3DSeries clearly exist, e.g. the Surface3D is displayed by the app.
main.cpp is searching this root object:
QObject *root = qobject_cast<QObject *>(engine.rootObjects().value(0));
All qml objects should be children of that root, is that right?
-
Listing all children of the root object, I do not see one of type QSurface3DSeries, even though the qml declares a Surface3DSeries and Surface3D is displayed with the data specified by the series.
I've modified main.cpp to list names and types of all objects returned by root->findChildren():
qDebug() << "found " << children.size() << "children"; for (int i = 0; i < children.size(); i++) { qDebug() << "child name " << children[i]->objectName() << ", class " << children[i]->metaObject()->className(); }
But there is no child of type QSurface3DSeries under root,:
found 9 children child name "" , class QQuickRootItem child name "myItem" , class QQuickItem child name "mySurface" , class QtDataVisualization::DeclarativeSurface child name "" , class QQmlListModel child name "myButton" , class QQuickButton child name "" , class QQuickRectangle child name "" , class QQuickPen child name "" , class QQuickIconLabel child name "label" , class QQuickMnemonicLabel
-
@tom-asso
I would not know about the "all QML objects being children of the root", but it does not seem unreasonable.For the
Window
/myWindow
one, because that's the top-level node maybe that is whatengine.rootObjects().value(0)
is, and thereforefindChild()
does not find it because it looks one level down, I don't know.If
engine.rootObjects()
can be multiple objects you could always try searching each of them just in case.For the
SurfaceSeries
/mySurfaceSeries
, if it were me I'd just try with a plainfindChild<QSurface3DSeries*>()
, no name, to find any.EDIT
My reply has crossed with your latest. You are doing the right thing by debug-walking the hierarchy. You need to go down now into"mySurface"
and see what is there. -
Checked to see if mySurface has children and it does not.
My ultimate goal is to access Surface3DSeries from C++. I'm thinking I could do that by accessing the mySurface object, but my debug shows mySurface is of type QtDataVisualization::DeclarativeSurface, which does not seem to be documented. :-( -
Checked to see if mySurface has children and it does not.
So the implementation does not just create in the fashion you hoped/expected. They have wrapped it away differently from the declarative.
My ultimate goal is to access Surface3DSeries from C++.
You might want to start a new thread for this, as it's very different from current title. Though expert people may see it there anyway.
QtDataVisualization::DeclarativeSurface
, which does not seem to be documentedAnd so probably not intended to be used. But you could look at https://code.woboq.org/qt5/qtdatavis3d/src/datavisualizationqml2/declarativesurface.cpp.html, it has functions like
QQmlListProperty<QSurface3DSeries> DeclarativeSurface::seriesList()
which presumably accesses the series. Use at your own risk.