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. How to dynamically pair qml objects with c++ objects
Forum Updated to NodeBB v4.3 + New Features

How to dynamically pair qml objects with c++ objects

Scheduled Pinned Locked Moved Solved QML and Qt Quick
29 Posts 5 Posters 375 Views 1 Watching
  • 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.
  • J Jay_emissary

    @Bob64 Oh, I have one, quick question if you don't mind: how can I pass any arguments the c++ object's constructor? This is what I suspect caused the crash: the constructor gets called as QML creates the c++ instance, but I never passed in any arguments, leaving any parameters (GameInitializer* init) as null.

    HitObject::HitObject(QObject *parent,GameInitializer* init)
    {
        qDebug() <<"Spawned";
        connect(init->deltaTimer, &TimeStep::Tick, this, &HitObject::Tick);
    }
    

    That said, connect gets called with a nullptr, and the game crashes. How can I make sure each C++ instance gets a pointer argument passed in? Or is this not possible using the QML->C++ method? I think I have some workarounds...

    B Offline
    B Offline
    Bob64
    wrote last edited by
    #12

    @Jay_emissary What Grecko is suggesting is what I originally had in mind when I first answered your other thread. There are various ways to do things in QML when it comes to exposing C++ functionality, and which is the best approach can depend on the details. In a larger application you will often end up using a variety of different approaches to fit different needs. The "C++ object instantiable as a QML object" is a useful approach to have in your toolbox, but the constructor argument issue adds a complication. The model-repeater approach gives you more control over the construction of the backend objects. The key thing is whether you can see a way to incorporate a Repeater into your QML. If you can do this, it honestly does simplify and clean things up because all the dynamic construction of QML objects is taken care of.

    J 1 Reply Last reply
    1
    • J Jay_emissary

      @Jay_emissary

      If I understand correctly, I'm placing registered C++ code into QML
      1c9bf43c-7e19-4512-8465-9cb4621f6aab-image.png

      This is from the qml file for hitObject (it serves as the blueprint for every object I'm creating dynamically):
      2ac9cd6b-bfb0-4227-8e85-3809265511d7-image.png

      As you can see, I'm setting up that c++ object here. And because I'm placing the backend in the QML file, I'll be using approach 1 from the previous reply (emit a signal to create the QML object)

      This causes a crash with no explanation in the application output. Say the code did work, should I expect to receive the message from HitObject's cpp constructor, for every created QML object? I would prefer to, so I can verify that the backend is up and running.

      HitObject::HitObject(QObject *parent)
      {
          connect(Timer, &TimeStep::Tick, this, &HitObject::Tick);
          qDebug() <<"Spawned";
      }
      

      I hope this explanation clears things up. Please feel free to let me know if there are any misconceptions.

      JKSHJ Online
      JKSHJ Online
      JKSH
      Moderators
      wrote last edited by
      #13

      @Jay_emissary I'm glad to hear that you've made good progress!

      I have another suggestion: Do not call qmlRegister*() functions to register your QML type at runtime. Instead, use the QML_ELEMENT macro to register your QML type at compile-time -- see https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html#registering-an-instantiable-object-type

      I didn't know connect could cause ambiguous crashes like the one I was experiencing.

      The connect() function didn't cause the crash. Dereferencing an invalid pointer caused the crash.

      how can I pass any arguments the c++ object's constructor?

      You cannot (at least not with current versions of Qt). QML-instantiable QObjects need to be default-constructible, which means you need to allow this to be called in C++: auto obj = new HitObject;

      With the example code that you gave, you can remove the GameInitializer* parameter from your constructor. Let the code that creates your HitObject also connect TimeStep::Tick() to HitObject::Tick().

      Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

      1 Reply Last reply
      3
      • GrecKoG GrecKo

        Use the model/view paradigm. Your backend shouldn't be aware of your QML code.

        In your spawnNote create a new object and add it to a model.
        In QML use a Repeater if you need a visual representation of your backend object. You can then bind to your object with one of the roles you exposed in your model.

        You don't need to instantiate a backend object in QML.

        J Offline
        J Offline
        Jay_emissary
        wrote last edited by
        #14

        @GrecKo Good evening,
        I've spent hours today researching and trying to implement the model system into my project. I did run into a couple of issues with this "simple" approach, though. Here's what I've done:

        I use this function to start the object creation process

        void GameManager::spawnNote(int index)
        {
            emit createHitObject();
        }
        

        In main.qml I instantiate the C++ AbstractListModel and call the C++ function, createHitObject() to spawn the hitObjects.

            HitObjectModel{
                id:hitObjModel
            }
        
            Repeater{
                model: hitObjModel
                delegate:  Rectangle {
                        id: image      
                        width: 10
                        height: 40
                        color: "white"
                        x: 100
                        y:100}
            }
        
            Connections{
                target: gameManager
               function onCreateHitObject ()
                        {
                            hitObjModel.createHitObject()
                        }
            }
        

        Now let's take a look at hitObjModel.createHitObject() from c++'s side:

        void HitObjectModel::createHitObject()
        {
            beginInsertRows(QModelIndex(),objects.size(),objects.size());
            objects.append(new HitObject());
            endInsertRows();
        
        }
        

        I use this code to ensure that the repeater spawns enough visual elements to pair with each HitObject. This successfully puts the rectangles on the screen, and I can control each of their functions from c++.

        J 1 Reply Last reply
        0
        • J Jay_emissary

          @GrecKo Good evening,
          I've spent hours today researching and trying to implement the model system into my project. I did run into a couple of issues with this "simple" approach, though. Here's what I've done:

          I use this function to start the object creation process

          void GameManager::spawnNote(int index)
          {
              emit createHitObject();
          }
          

          In main.qml I instantiate the C++ AbstractListModel and call the C++ function, createHitObject() to spawn the hitObjects.

              HitObjectModel{
                  id:hitObjModel
              }
          
              Repeater{
                  model: hitObjModel
                  delegate:  Rectangle {
                          id: image      
                          width: 10
                          height: 40
                          color: "white"
                          x: 100
                          y:100}
              }
          
              Connections{
                  target: gameManager
                 function onCreateHitObject ()
                          {
                              hitObjModel.createHitObject()
                          }
              }
          

          Now let's take a look at hitObjModel.createHitObject() from c++'s side:

          void HitObjectModel::createHitObject()
          {
              beginInsertRows(QModelIndex(),objects.size(),objects.size());
              objects.append(new HitObject());
              endInsertRows();
          
          }
          

          I use this code to ensure that the repeater spawns enough visual elements to pair with each HitObject. This successfully puts the rectangles on the screen, and I can control each of their functions from c++.

          J Offline
          J Offline
          Jay_emissary
          wrote last edited by
          #15

          @Jay_emissary Here's where the complexity comes in:
          I need to reference deltatimer, a QTimer that's instantiated from another class. Each object should have this connect function attached, so it fires the tick function for each corresponding object. Essentially, this code:

          • HitObjectModel.cpp
             HitObject* hitObj = new HitObject();
              beginInsertRows(QModelIndex(),objects.size(),objects.size());
              objects.append(hitObj);
              endInsertRows();
              connect(deltaTimer, &TimeStep::Tick,hitObj, &HitObject::Tick);
          

          The only way to access a C++ object created by another class is by passing it into the model, but since the model is created from QML, there is no way for it to access deltatimer from the "initializer" class.

          My hope is that I can create the model in C++ instead and expose it to QML. That way, I can pass in the timer reference and use it across all the created objects.

          This is from the initializer class:

              deltaTimer = new TimeStep();
              hitModelObj = new HitObjectModel(this,deltaTimer);
              engine->rootContext()->setContextProperty("HitObjectModel", hitModelObj);
          

          Now I have the deltaTimer reference. I should be able to access the model in QML too, no?

          I'll also call createHitObject() from the model directly from the spawnNote function instead of emitting a signal like earlier

          void GameManager::spawnNote(int index)
          {
              hitObjectModel->createHitObject();
          

          in QML all I should need is a reference to the c++ model, which should be exposed now right?

          Repeater{
                  model: hitObjModel
                   delegate:  Rectangle {
                                  id: image      
                                  width: 10
                                  height: 40
                                  color: "white"
                                  x: 100
                                  y:100}
                      }
          

          For some reason, Qt throws me an error: Main.qml:25: ReferenceError: hitObjModel is not defined

          How doesn't Qt recognize the model after it's created??

          Is what I'm trying to do impossible to execute in Qt? I'm at a loss...

          B 1 Reply Last reply
          0
          • B Bob64

            @Jay_emissary What Grecko is suggesting is what I originally had in mind when I first answered your other thread. There are various ways to do things in QML when it comes to exposing C++ functionality, and which is the best approach can depend on the details. In a larger application you will often end up using a variety of different approaches to fit different needs. The "C++ object instantiable as a QML object" is a useful approach to have in your toolbox, but the constructor argument issue adds a complication. The model-repeater approach gives you more control over the construction of the backend objects. The key thing is whether you can see a way to incorporate a Repeater into your QML. If you can do this, it honestly does simplify and clean things up because all the dynamic construction of QML objects is taken care of.

            J Offline
            J Offline
            Jay_emissary
            wrote last edited by
            #16

            @Bob64 Gotcha! I'll be sure to take note of this whenever I'm deciding on how I might approach connecting C++ to QML in my projects.

            1 Reply Last reply
            0
            • J Jay_emissary

              @Jay_emissary Here's where the complexity comes in:
              I need to reference deltatimer, a QTimer that's instantiated from another class. Each object should have this connect function attached, so it fires the tick function for each corresponding object. Essentially, this code:

              • HitObjectModel.cpp
                 HitObject* hitObj = new HitObject();
                  beginInsertRows(QModelIndex(),objects.size(),objects.size());
                  objects.append(hitObj);
                  endInsertRows();
                  connect(deltaTimer, &TimeStep::Tick,hitObj, &HitObject::Tick);
              

              The only way to access a C++ object created by another class is by passing it into the model, but since the model is created from QML, there is no way for it to access deltatimer from the "initializer" class.

              My hope is that I can create the model in C++ instead and expose it to QML. That way, I can pass in the timer reference and use it across all the created objects.

              This is from the initializer class:

                  deltaTimer = new TimeStep();
                  hitModelObj = new HitObjectModel(this,deltaTimer);
                  engine->rootContext()->setContextProperty("HitObjectModel", hitModelObj);
              

              Now I have the deltaTimer reference. I should be able to access the model in QML too, no?

              I'll also call createHitObject() from the model directly from the spawnNote function instead of emitting a signal like earlier

              void GameManager::spawnNote(int index)
              {
                  hitObjectModel->createHitObject();
              

              in QML all I should need is a reference to the c++ model, which should be exposed now right?

              Repeater{
                      model: hitObjModel
                       delegate:  Rectangle {
                                      id: image      
                                      width: 10
                                      height: 40
                                      color: "white"
                                      x: 100
                                      y:100}
                          }
              

              For some reason, Qt throws me an error: Main.qml:25: ReferenceError: hitObjModel is not defined

              How doesn't Qt recognize the model after it's created??

              Is what I'm trying to do impossible to execute in Qt? I'm at a loss...

              B Offline
              B Offline
              Bob64
              wrote last edited by
              #17

              @Jay_emissary said in How to dynamically pair qml objects with c++ objects:

              "HitObjectModel"

              This is the exact name you should use in QML, not hitObjectModel.

              J 1 Reply Last reply
              1
              • B Bob64

                @Jay_emissary said in How to dynamically pair qml objects with c++ objects:

                "HitObjectModel"

                This is the exact name you should use in QML, not hitObjectModel.

                J Offline
                J Offline
                Jay_emissary
                wrote last edited by
                #18

                @Bob64 Oh, whoops! I must have overlooked that one. Thanks for catching that, lol... Now the QML objects are on the screen and the C++ objects get created as well!

                The only issue I have at this point is that I'm not so sure that the C++ object is associated with the QML objects. I added this statement to the HitObject's constructor:

                    qDebug() <<property("x").toDouble();
                

                And because I set the x to 100 using in the following code, I should expect to receive that value when the c++ object is created.

                
                    Repeater{
                        model: hitObjectModel
                        delegate:  Rectangle {
                                id: image      
                                width: 10
                                height: 40
                                color: "white"
                                x: 100
                                y:100}
                    }
                

                But instead, I'm getting a 0. Could this possibly be because the QML is only reacting to the fact my object list has increased, and less so the fact that a C++ object has been created to pair with the corresponding QML object?

                jsulmJ B 2 Replies Last reply
                0
                • J Jay_emissary

                  @Bob64 Oh, whoops! I must have overlooked that one. Thanks for catching that, lol... Now the QML objects are on the screen and the C++ objects get created as well!

                  The only issue I have at this point is that I'm not so sure that the C++ object is associated with the QML objects. I added this statement to the HitObject's constructor:

                      qDebug() <<property("x").toDouble();
                  

                  And because I set the x to 100 using in the following code, I should expect to receive that value when the c++ object is created.

                  
                      Repeater{
                          model: hitObjectModel
                          delegate:  Rectangle {
                                  id: image      
                                  width: 10
                                  height: 40
                                  color: "white"
                                  x: 100
                                  y:100}
                      }
                  

                  But instead, I'm getting a 0. Could this possibly be because the QML is only reacting to the fact my object list has increased, and less so the fact that a C++ object has been created to pair with the corresponding QML object?

                  jsulmJ Online
                  jsulmJ Online
                  jsulm
                  Lifetime Qt Champion
                  wrote last edited by
                  #19

                  @Jay_emissary said in How to dynamically pair qml objects with c++ objects:

                  But instead, I'm getting a 0

                  I'm not a QML expert, but maybe QML uses setters to set members like x?

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

                  J 1 Reply Last reply
                  0
                  • jsulmJ jsulm

                    @Jay_emissary said in How to dynamically pair qml objects with c++ objects:

                    But instead, I'm getting a 0

                    I'm not a QML expert, but maybe QML uses setters to set members like x?

                    J Offline
                    J Offline
                    Jay_emissary
                    wrote last edited by
                    #20

                    @jsulm As far as I know, yeah, you should be able to call a setProperty function in C++, and in QML you can directly set properties like x as well. Though, I'm currently trying to get x so I can verify that the c++ object is hooked up and can read QML's properties. It's important for me to get and set x and y properties so that I can perform functions such as smoothly moving objects across the screen at a framerate-dependent speed.

                    jsulmJ 1 Reply Last reply
                    0
                    • J Jay_emissary

                      @jsulm As far as I know, yeah, you should be able to call a setProperty function in C++, and in QML you can directly set properties like x as well. Though, I'm currently trying to get x so I can verify that the c++ object is hooked up and can read QML's properties. It's important for me to get and set x and y properties so that I can perform functions such as smoothly moving objects across the screen at a framerate-dependent speed.

                      jsulmJ Online
                      jsulmJ Online
                      jsulm
                      Lifetime Qt Champion
                      wrote last edited by
                      #21

                      @Jay_emissary said in How to dynamically pair qml objects with c++ objects:

                      I'm currently trying to get x so I can verify that the c++ object is hooked up and can read QML's properties

                      But you wrote: "I added this statement to the HitObject's constructor". This will print 0, because when the parameter-less constructor is called 100 was not yet assigned to x.

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

                      J 1 Reply Last reply
                      0
                      • J Jay_emissary

                        @Bob64 Oh, whoops! I must have overlooked that one. Thanks for catching that, lol... Now the QML objects are on the screen and the C++ objects get created as well!

                        The only issue I have at this point is that I'm not so sure that the C++ object is associated with the QML objects. I added this statement to the HitObject's constructor:

                            qDebug() <<property("x").toDouble();
                        

                        And because I set the x to 100 using in the following code, I should expect to receive that value when the c++ object is created.

                        
                            Repeater{
                                model: hitObjectModel
                                delegate:  Rectangle {
                                        id: image      
                                        width: 10
                                        height: 40
                                        color: "white"
                                        x: 100
                                        y:100}
                            }
                        

                        But instead, I'm getting a 0. Could this possibly be because the QML is only reacting to the fact my object list has increased, and less so the fact that a C++ object has been created to pair with the corresponding QML object?

                        B Offline
                        B Offline
                        Bob64
                        wrote last edited by
                        #22

                        @Jay_emissary @Jay_emissary There is no implicit association of the C++ object with the QML object. Each object in your "view" is associated with a known index into the model and can access whatever "data" the model exposes at that index. In your case I believe you are exposing a whole object as your data, but often the underlying items in a model are more encapsulated and you just expose certain named fields at each index.

                        Essentially there is a one way data flow from the model to the view. This doesn't mean that you can't expose functions from your model that can be used to update it. This would have to follow a strict process of updating the model items and providing appropriate signals to notify the view to update itself from the model.

                        One simple thing that is worth asking: would it make sense for the X and Y positions to be define by properties of your model object?

                        Otherwise, maybe it would help if you had another go at outlining what you are trying to achieve. For example, even if you don't know exactly how to write it, what is that you want to achieve in the QML layer and how will it relate to the C++ objects in the backend?

                        J 1 Reply Last reply
                        1
                        • jsulmJ jsulm

                          @Jay_emissary said in How to dynamically pair qml objects with c++ objects:

                          I'm currently trying to get x so I can verify that the c++ object is hooked up and can read QML's properties

                          But you wrote: "I added this statement to the HitObject's constructor". This will print 0, because when the parameter-less constructor is called 100 was not yet assigned to x.

                          J Offline
                          J Offline
                          Jay_emissary
                          wrote last edited by Jay_emissary
                          #23

                          @jsulm I see… I initially thought that the QML object gets created first, allowing me to then view the properties via C++. Apparently, the C++ object can be created and called upon a bit earlier. Thanks for pointing that out. I’ll keep that in mind.

                          1 Reply Last reply
                          0
                          • B Bob64

                            @Jay_emissary @Jay_emissary There is no implicit association of the C++ object with the QML object. Each object in your "view" is associated with a known index into the model and can access whatever "data" the model exposes at that index. In your case I believe you are exposing a whole object as your data, but often the underlying items in a model are more encapsulated and you just expose certain named fields at each index.

                            Essentially there is a one way data flow from the model to the view. This doesn't mean that you can't expose functions from your model that can be used to update it. This would have to follow a strict process of updating the model items and providing appropriate signals to notify the view to update itself from the model.

                            One simple thing that is worth asking: would it make sense for the X and Y positions to be define by properties of your model object?

                            Otherwise, maybe it would help if you had another go at outlining what you are trying to achieve. For example, even if you don't know exactly how to write it, what is that you want to achieve in the QML layer and how will it relate to the C++ objects in the backend?

                            J Offline
                            J Offline
                            Jay_emissary
                            wrote last edited by
                            #24

                            @Bob64

                            @Bob64 said in How to dynamically pair qml objects with c++ objects:

                            There is no implicit association of the C++ object with the QML object. Each object in your "view" is associated with a known index into the model and can access whatever "data" the model exposes at that index. In your case I believe you are exposing a whole object as your data, but often the underlying items in a model are more encapsulated and you just expose certain named fields at each index.

                            Ohhh gotcha. Given this information, I'll need to thoroughly think about how I can approach adding precise movement my objects while remaining within these guidelines

                            @Bob64 said in How to dynamically pair qml objects with c++ objects:

                            One simple thing that is worth asking: would it make sense for the X and Y positions to be define by properties of your model object?

                            I don't think so, since each object needs to have its own independent x and y values.

                            @Bob64 said in How to dynamically pair qml objects with c++ objects:

                            Otherwise, maybe it would help if you had another go at outlining what you are trying to achieve. For example, even if you don't know exactly how to write it, what is that you want to achieve in the QML layer and how will it relate to the C++ objects in the backend?

                            For some extra context, I'm creating a rhythm game in Qt. The HitObject/Notes are "falling" objects that the player "hits" to score points. Check https://www.youtube.com/watch?v=RSWoIsXfoM4
                            at 1:12. The vertical scrolling arrows in the footage are essentially what I'm creating at the moment. Each arrow has their own X and Y coordinate and in video games you usually need a timestep/deltatime system to ensure that every object moves consistently across all platforms. This is why I emphasize accessing the deltaTimer object I've created in c++.

                            But I think your explanation of the QML-> C++ workflow gives me a lot to consider, and honestly, probably answers the remaining questions I have about the process. I'm currently not in a place to hop on Qt just yet, but I'll continue to look into this later today. Oh, by the way, I know that I've been keeping this thread going for a while. Would you say it's best to just start a new one if I have another topic or one that closely resembles this one?

                            Regardless, many thanks to you and everyone for having patience with me and providing more insight into the QML->C++ workflow. This is a topic that has had me puzzled for a while now, haha. Glad I have a better grasp on it now.

                            1 Reply Last reply
                            0
                            • J Jay_emissary has marked this topic as solved
                            • GrecKoG Offline
                              GrecKoG Offline
                              GrecKo
                              Qt Champions 2018
                              wrote last edited by
                              #25

                              I don't think so, since each object needs to have its own independent x and y values.

                              Your "hit objects" position will be handled by your gameplay loop, these should be definitely be defined by your model. At least a "column" value and a "yProgress" value.

                              J 1 Reply Last reply
                              2
                              • GrecKoG GrecKo

                                I don't think so, since each object needs to have its own independent x and y values.

                                Your "hit objects" position will be handled by your gameplay loop, these should be definitely be defined by your model. At least a "column" value and a "yProgress" value.

                                J Offline
                                J Offline
                                Jay_emissary
                                wrote last edited by
                                #26

                                @GrecKo I get the model determining a column value, but why yProgress? Shouldn't each element move independently of the other?

                                1 Reply Last reply
                                0
                                • B Offline
                                  B Offline
                                  Bob64
                                  wrote last edited by
                                  #27

                                  The way I understood it is that each element has its own entry in the model and this entry provides its own column index and yProgress. One option is that yProgress is a number between 0 and 1 that represents how far down the screen the object is. The delegate converts the column index and the progress number to an x-y coordinate that is meaningful in the geometry of the UI.

                                  J 1 Reply Last reply
                                  2
                                  • B Bob64

                                    The way I understood it is that each element has its own entry in the model and this entry provides its own column index and yProgress. One option is that yProgress is a number between 0 and 1 that represents how far down the screen the object is. The delegate converts the column index and the progress number to an x-y coordinate that is meaningful in the geometry of the UI.

                                    J Offline
                                    J Offline
                                    Jay_emissary
                                    wrote last edited by
                                    #28

                                    @Bob64 Understood. I can see how that works now. Thanks!

                                    1 Reply Last reply
                                    0
                                    • GrecKoG Offline
                                      GrecKoG Offline
                                      GrecKo
                                      Qt Champions 2018
                                      wrote last edited by
                                      #29

                                      Yes that's exactly what I meant, thanks for explaining it Bob64.

                                      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