Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QObject::findChild() - "simple" case not working
Forum Updated to NodeBB v4.3 + New Features

QObject::findChild() - "simple" case not working

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
12 Posts 3 Posters 11.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Tom assoT Offline
    Tom assoT Offline
    Tom asso
    wrote on last edited by Tom asso
    #1

    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 reason

    main.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

    jsulmJ 1 Reply Last reply
    0
    • Tom assoT Tom asso

      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 reason

      main.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

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @tom-asso topographySeries != topographicSeries

      objectName: "topographySeries"
      ...
      QObject *object = g_rootWindow->findChild<QObject *>("topographicSeries");
      

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      3
      • Tom assoT Offline
        Tom assoT Offline
        Tom asso
        wrote on last edited by Tom asso
        #3

        @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.

        1 Reply Last reply
        0
        • Tom assoT Offline
          Tom assoT Offline
          Tom asso
          wrote on last edited by
          #4

          @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... :-(

          jsulmJ 1 Reply Last reply
          0
          • Tom assoT Tom asso

            @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... :-(

            jsulmJ Offline
            jsulmJ Offline
            jsulm
            Lifetime Qt Champion
            wrote on last edited by
            #5

            @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.

            https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            1
            • Tom assoT Offline
              Tom assoT Offline
              Tom asso
              wrote on last edited by
              #6

              @jsulm - added sleep(2) right after engine.load(url) in main.cpp - does not alter the result...
              Anyone able to reproduce my results?
              Thanks!

              JonBJ 1 Reply Last reply
              0
              • Tom assoT Tom asso

                @jsulm - added sleep(2) right after engine.load(url) in main.cpp - does not alter the result...
                Anyone able to reproduce my results?
                Thanks!

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #7

                @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) your sleep(2) won't help! If you wish to check the issue he suggests I think you do need to set up a QTimer.

                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.

                1 Reply Last reply
                2
                • Tom assoT Offline
                  Tom assoT Offline
                  Tom asso
                  wrote on last edited by Tom asso
                  #8

                  @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?

                  JonBJ 1 Reply Last reply
                  0
                  • Tom assoT Offline
                    Tom assoT Offline
                    Tom asso
                    wrote on last edited by
                    #9

                    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
                    
                    1 Reply Last reply
                    0
                    • Tom assoT Tom asso

                      @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?

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by JonB
                      #10

                      @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 what engine.rootObjects().value(0) is, and therefore findChild() 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 plain findChild<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.

                      1 Reply Last reply
                      0
                      • Tom assoT Offline
                        Tom assoT Offline
                        Tom asso
                        wrote on last edited by
                        #11

                        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. :-(

                        JonBJ 1 Reply Last reply
                        0
                        • Tom assoT Tom asso

                          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. :-(

                          JonBJ Offline
                          JonBJ Offline
                          JonB
                          wrote on last edited by JonB
                          #12

                          @tom-asso

                          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 documented

                          And 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.

                          1 Reply Last reply
                          1

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved