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. Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?
QtWS25 Last Chance

Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?

Scheduled Pinned Locked Moved Solved QML and Qt Quick
15 Posts 4 Posters 7.5k 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.
  • C Offline
    C Offline
    Curtwagner1984
    wrote on 2 Jun 2017, 16:05 last edited by
    #1

    Hello!

    The core of my problem is this:

    I want to use different instances of the same subclasses of QAbstractItemModel.
    To that end, I understand I need to register the class like so qmlRegisterType<MySubClass>("com.me.qmlcomponents", 1, 0, "MySubClass");

    However, my class doesn't have a default constructor because I need it to talk to a SQLite database so I send a pointer to a database manager class in the constructor, like so :

    BasicListModel::BasicListModel(DbManager *dbManager)
     {
      this->dbManager = dbManager;
      this->items = QList<QMap<QString, QVariant>>();
     }
    

    So this is my question, Is it possible to register a class that doesn't have a default constructor with QML ?

    Alternatively, is it possible to pass a pointer to the database manager class to an object instantiated by QML and registered with qmlRegisterType ?

    S C 2 Replies Last reply 3 Jun 2017, 07:09
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 2 Jun 2017, 20:58 last edited by
      #2

      Hi,

      What exactly does that database manager do ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      C 1 Reply Last reply 3 Jun 2017, 10:46
      0
      • C Curtwagner1984
        2 Jun 2017, 16:05

        Hello!

        The core of my problem is this:

        I want to use different instances of the same subclasses of QAbstractItemModel.
        To that end, I understand I need to register the class like so qmlRegisterType<MySubClass>("com.me.qmlcomponents", 1, 0, "MySubClass");

        However, my class doesn't have a default constructor because I need it to talk to a SQLite database so I send a pointer to a database manager class in the constructor, like so :

        BasicListModel::BasicListModel(DbManager *dbManager)
         {
          this->dbManager = dbManager;
          this->items = QList<QMap<QString, QVariant>>();
         }
        

        So this is my question, Is it possible to register a class that doesn't have a default constructor with QML ?

        Alternatively, is it possible to pass a pointer to the database manager class to an object instantiated by QML and registered with qmlRegisterType ?

        S Offline
        S Offline
        sierdzio
        Moderators
        wrote on 3 Jun 2017, 07:09 last edited by
        #3

        @Curtwagner1984 said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

        So this is my question, Is it possible to register a class that doesn't have a default constructor with QML ?

        No.

        You can, however, add an init() function and pass your pointer there, after QML engine creates the object.

        Or you can instantiate your object on C++ side (calling any constructor you like) and pass it to QML via context, or via some other object (returning BasicListModel pointer from some method).

        Alternatively, is it possible to pass a pointer to the database manager class to an object instantiated by QML and registered with qmlRegisterType ?

        Yes, and there are many ways to do it. Is your DbManager a QObject?

        (Z(:^

        1 Reply Last reply
        0
        • C Curtwagner1984
          2 Jun 2017, 16:05

          Hello!

          The core of my problem is this:

          I want to use different instances of the same subclasses of QAbstractItemModel.
          To that end, I understand I need to register the class like so qmlRegisterType<MySubClass>("com.me.qmlcomponents", 1, 0, "MySubClass");

          However, my class doesn't have a default constructor because I need it to talk to a SQLite database so I send a pointer to a database manager class in the constructor, like so :

          BasicListModel::BasicListModel(DbManager *dbManager)
           {
            this->dbManager = dbManager;
            this->items = QList<QMap<QString, QVariant>>();
           }
          

          So this is my question, Is it possible to register a class that doesn't have a default constructor with QML ?

          Alternatively, is it possible to pass a pointer to the database manager class to an object instantiated by QML and registered with qmlRegisterType ?

          C Offline
          C Offline
          c64zottel
          wrote on 3 Jun 2017, 07:35 last edited by c64zottel 6 Mar 2017, 07:37
          #4

          @Curtwagner1984 You can register classes without default ctor like:

          qmlRegisterUncreatableType<InputDeviceConfigurator>(
                          "InputDeviceConfigurator", 1, 0, "InputDeviceConfigurator", "Not creatable in Qml." );
          

          But then you can not create an instance of that class inside Qml via:

          InputDeviceConfigurator {
          id: myFatDevice
          }
          

          So, you have to pass a pointer to your model.
          You may also be interested in the little discussion here: https://forum.qt.io/topic/79725/how-to-use-q_disable_copy-class-without-ctor

          1 Reply Last reply
          1
          • S SGaist
            2 Jun 2017, 20:58

            Hi,

            What exactly does that database manager do ?

            C Offline
            C Offline
            Curtwagner1984
            wrote on 3 Jun 2017, 10:46 last edited by
            #5

            @SGaist said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

            Hi,

            What exactly does that database manager do ?

            It basically executes arbitrary SQL and parses the results into something my model can use. For example, I have a movie model, it has a search function. When the search function is called, it's calling the dbManager with a prepared SQL statement that returns a list of QMap<QString, QVariant> for each row of the search result where each value in the QMap corresponds to a column of the SQL table. I later use that list to implement the data() function of the model like so:

            QVariant MovieModel::data(const QModelIndex &index, int role) const
            {
                if (!index.isValid()){
                        return QVariant();
                    }
            
                    QMap<QString,QVariant> currentItem = this->items.at(index.row());
            
                    if (role == IdRole){
                        return currentItem["id"];
                    }else if (role == ThumbRole){
                        return currentItem["thumbnail"];
                    }else if (role == YearRole){
                        return currentItem["year"];
                    }else{
                      return QVariant();
                    }
            }
            

            @sierdzio said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

            You can, however, add an init() function and pass your pointer there, after QML engine creates the object.

            How would I go about doing this? How would I know that the object was created in QML in order to trigger the init() function?
            I suppose I could connect it to a signal that is triggered by the default constructor, is that what you are suggesting?

            Or you can instantiate your object on C++ side (calling any constructor you like) and pass it to QML via context, or via some other object (returning BasicListModel pointer from some method).

            You mean something like exposing another object using setContextProperty that has a Q_INVOKABLE method along the lines of :

            BasicListModel* QmlComm::newListModel() {
              return new BasicListModel(this.dbManager);
            }
            

            and then in QML using it like this?

            ListView{
              id:myList
              model:qmlComm.newListModel();
            }
            

            Yes, and there are many ways to do it. Is your DbManager a QObject?

            Indeed it is.

            @c64zottel But the whole point of me doing this is that I want to be able to create different instances of the model from QML.

            C S 2 Replies Last reply 3 Jun 2017, 11:14
            0
            • C Curtwagner1984
              3 Jun 2017, 10:46

              @SGaist said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

              Hi,

              What exactly does that database manager do ?

              It basically executes arbitrary SQL and parses the results into something my model can use. For example, I have a movie model, it has a search function. When the search function is called, it's calling the dbManager with a prepared SQL statement that returns a list of QMap<QString, QVariant> for each row of the search result where each value in the QMap corresponds to a column of the SQL table. I later use that list to implement the data() function of the model like so:

              QVariant MovieModel::data(const QModelIndex &index, int role) const
              {
                  if (!index.isValid()){
                          return QVariant();
                      }
              
                      QMap<QString,QVariant> currentItem = this->items.at(index.row());
              
                      if (role == IdRole){
                          return currentItem["id"];
                      }else if (role == ThumbRole){
                          return currentItem["thumbnail"];
                      }else if (role == YearRole){
                          return currentItem["year"];
                      }else{
                        return QVariant();
                      }
              }
              

              @sierdzio said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

              You can, however, add an init() function and pass your pointer there, after QML engine creates the object.

              How would I go about doing this? How would I know that the object was created in QML in order to trigger the init() function?
              I suppose I could connect it to a signal that is triggered by the default constructor, is that what you are suggesting?

              Or you can instantiate your object on C++ side (calling any constructor you like) and pass it to QML via context, or via some other object (returning BasicListModel pointer from some method).

              You mean something like exposing another object using setContextProperty that has a Q_INVOKABLE method along the lines of :

              BasicListModel* QmlComm::newListModel() {
                return new BasicListModel(this.dbManager);
              }
              

              and then in QML using it like this?

              ListView{
                id:myList
                model:qmlComm.newListModel();
              }
              

              Yes, and there are many ways to do it. Is your DbManager a QObject?

              Indeed it is.

              @c64zottel But the whole point of me doing this is that I want to be able to create different instances of the model from QML.

              C Offline
              C Offline
              c64zottel
              wrote on 3 Jun 2017, 11:14 last edited by
              #6

              @Curtwagner1984 I just tried to pass the id as "this" pointer and it worked.

              So, you register all your derived classes as usual and use them with a proper id.
              The generic function takes a pointer of the base class, and you call that function with the id you issued in Qml.

              C 1 Reply Last reply 3 Jun 2017, 11:56
              0
              • C c64zottel
                3 Jun 2017, 11:14

                @Curtwagner1984 I just tried to pass the id as "this" pointer and it worked.

                So, you register all your derived classes as usual and use them with a proper id.
                The generic function takes a pointer of the base class, and you call that function with the id you issued in Qml.

                C Offline
                C Offline
                Curtwagner1984
                wrote on 3 Jun 2017, 11:56 last edited by
                #7

                @c64zottel said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                @Curtwagner1984 I just tried to pass the id as "this" pointer and it worked.

                So, you register all your derived classes as usual and use them with a proper id.
                The generic function takes a pointer of the base class, and you call that function with the id you issued in Qml.

                Can you please elaborate on what exactly you did? I'm not sure I follow. An example would be best.

                C 1 Reply Last reply 3 Jun 2017, 12:11
                0
                • C Curtwagner1984
                  3 Jun 2017, 11:56

                  @c64zottel said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                  @Curtwagner1984 I just tried to pass the id as "this" pointer and it worked.

                  So, you register all your derived classes as usual and use them with a proper id.
                  The generic function takes a pointer of the base class, and you call that function with the id you issued in Qml.

                  Can you please elaborate on what exactly you did? I'm not sure I follow. An example would be best.

                  C Offline
                  C Offline
                  c64zottel
                  wrote on 3 Jun 2017, 12:11 last edited by
                  #8

                  @Curtwagner1984 Something like this:

                  class Base : public QObject
                  {
                  ...
                    Q_INVOKABLE virtual void func();
                  
                  signals:
                    void activated();
                  }
                  
                  class D1 : public Base
                  {
                      void func() {
                          do stuff;
                      }
                  }
                  
                  class D2 : public Base
                  {
                      void func() {
                          do other stuff;
                      }
                  }
                  
                  class SomeClientUsingBase : public QObject
                  {
                      Q_INVOKABLE void functionDealingWithSomethingDerivedFromBase( Base * b ) {
                          b->func();  // uses either D1 or D2
                      }
                  }
                  

                  In Qml:

                  qmlRegisterType<SomeClientUsingBase>("com.me.qmlcomponents", 1, 0, "Client");
                  qmlRegisterType<Base>("com.me.qmlcomponents", 1, 0, "Base");
                  qmlRegisterType<D1>("com.me.qmlcomponents", 1, 0, "D1");
                  qmlRegisterType<D2>("com.me.qmlcomponents", 1, 0, "D2");
                  
                  Client {
                      id: waitingToUseBase
                  }
                  
                  D1 {
                      id: d1
                      onActivated: waitingToUseBase.functionDealingWithSomethingDerivedFromBase( d1 )
                  }
                  
                  D2 {
                      id: d2
                      onActivated: waitingToUseBase.functionDealingWithSomethingDerivedFromBase( d1 )
                  }
                  
                  C 1 Reply Last reply 4 Jun 2017, 11:59
                  0
                  • C Curtwagner1984
                    3 Jun 2017, 10:46

                    @SGaist said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                    Hi,

                    What exactly does that database manager do ?

                    It basically executes arbitrary SQL and parses the results into something my model can use. For example, I have a movie model, it has a search function. When the search function is called, it's calling the dbManager with a prepared SQL statement that returns a list of QMap<QString, QVariant> for each row of the search result where each value in the QMap corresponds to a column of the SQL table. I later use that list to implement the data() function of the model like so:

                    QVariant MovieModel::data(const QModelIndex &index, int role) const
                    {
                        if (!index.isValid()){
                                return QVariant();
                            }
                    
                            QMap<QString,QVariant> currentItem = this->items.at(index.row());
                    
                            if (role == IdRole){
                                return currentItem["id"];
                            }else if (role == ThumbRole){
                                return currentItem["thumbnail"];
                            }else if (role == YearRole){
                                return currentItem["year"];
                            }else{
                              return QVariant();
                            }
                    }
                    

                    @sierdzio said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                    You can, however, add an init() function and pass your pointer there, after QML engine creates the object.

                    How would I go about doing this? How would I know that the object was created in QML in order to trigger the init() function?
                    I suppose I could connect it to a signal that is triggered by the default constructor, is that what you are suggesting?

                    Or you can instantiate your object on C++ side (calling any constructor you like) and pass it to QML via context, or via some other object (returning BasicListModel pointer from some method).

                    You mean something like exposing another object using setContextProperty that has a Q_INVOKABLE method along the lines of :

                    BasicListModel* QmlComm::newListModel() {
                      return new BasicListModel(this.dbManager);
                    }
                    

                    and then in QML using it like this?

                    ListView{
                      id:myList
                      model:qmlComm.newListModel();
                    }
                    

                    Yes, and there are many ways to do it. Is your DbManager a QObject?

                    Indeed it is.

                    @c64zottel But the whole point of me doing this is that I want to be able to create different instances of the model from QML.

                    S Offline
                    S Offline
                    sierdzio
                    Moderators
                    wrote on 4 Jun 2017, 08:13 last edited by
                    #9

                    @sierdzio said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                    You can, however, add an init() function and pass your pointer there, after QML engine creates the object.

                    How would I go about doing this? How would I know that the object was created in QML in order to trigger the init() function?
                    I suppose I could connect it to a signal that is triggered by the default constructor, is that what you are suggesting?

                    Ugh, as always with QML, there are many ways to do it and I never know which solution to recommend :D Let's try this:

                    // C++
                    // add DbManager to QML context somewhere. Example:
                    DbManager manager;
                    QQmlApplicationEngine engine;
                    engine.rootContext()->setContextProperty("Manager", &manager);
                    [...]
                    class BasicListModel
                    {
                      Q_INVOKABLE void init(DbManager *manager);
                    }
                    
                    // QML
                    [...]
                    model: BasicListModel {
                      Component.onCompleted: {
                        init(manager);
                      }
                    }
                    

                    (Z(:^

                    C 1 Reply Last reply 4 Jun 2017, 13:52
                    1
                    • C c64zottel
                      3 Jun 2017, 12:11

                      @Curtwagner1984 Something like this:

                      class Base : public QObject
                      {
                      ...
                        Q_INVOKABLE virtual void func();
                      
                      signals:
                        void activated();
                      }
                      
                      class D1 : public Base
                      {
                          void func() {
                              do stuff;
                          }
                      }
                      
                      class D2 : public Base
                      {
                          void func() {
                              do other stuff;
                          }
                      }
                      
                      class SomeClientUsingBase : public QObject
                      {
                          Q_INVOKABLE void functionDealingWithSomethingDerivedFromBase( Base * b ) {
                              b->func();  // uses either D1 or D2
                          }
                      }
                      

                      In Qml:

                      qmlRegisterType<SomeClientUsingBase>("com.me.qmlcomponents", 1, 0, "Client");
                      qmlRegisterType<Base>("com.me.qmlcomponents", 1, 0, "Base");
                      qmlRegisterType<D1>("com.me.qmlcomponents", 1, 0, "D1");
                      qmlRegisterType<D2>("com.me.qmlcomponents", 1, 0, "D2");
                      
                      Client {
                          id: waitingToUseBase
                      }
                      
                      D1 {
                          id: d1
                          onActivated: waitingToUseBase.functionDealingWithSomethingDerivedFromBase( d1 )
                      }
                      
                      D2 {
                          id: d2
                          onActivated: waitingToUseBase.functionDealingWithSomethingDerivedFromBase( d1 )
                      }
                      
                      C Offline
                      C Offline
                      Curtwagner1984
                      wrote on 4 Jun 2017, 11:59 last edited by
                      #10
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • S sierdzio
                        4 Jun 2017, 08:13

                        @sierdzio said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                        You can, however, add an init() function and pass your pointer there, after QML engine creates the object.

                        How would I go about doing this? How would I know that the object was created in QML in order to trigger the init() function?
                        I suppose I could connect it to a signal that is triggered by the default constructor, is that what you are suggesting?

                        Ugh, as always with QML, there are many ways to do it and I never know which solution to recommend :D Let's try this:

                        // C++
                        // add DbManager to QML context somewhere. Example:
                        DbManager manager;
                        QQmlApplicationEngine engine;
                        engine.rootContext()->setContextProperty("Manager", &manager);
                        [...]
                        class BasicListModel
                        {
                          Q_INVOKABLE void init(DbManager *manager);
                        }
                        
                        // QML
                        [...]
                        model: BasicListModel {
                          Component.onCompleted: {
                            init(manager);
                          }
                        }
                        
                        C Offline
                        C Offline
                        Curtwagner1984
                        wrote on 4 Jun 2017, 13:52 last edited by Curtwagner1984 6 Apr 2017, 14:09
                        #11

                        @sierdzio Thank you! It works exactly as expected.

                        Now I wonder, can I create an instance of BasicListModel dynamically? Something along the lines of :

                        function generateDynamicInstance()
                        {
                            // returns a new instance of BasicListModel
                        }
                        
                        Button {
                              onClicked:{
                                var newInstance = generateDynamicInstance();
                                myListView.model = newInstance;
                              }
                          
                        }
                        
                        ListView{
                            id:myListView
                        }
                        
                        S 1 Reply Last reply 4 Jun 2017, 14:21
                        0
                        • C Curtwagner1984
                          4 Jun 2017, 13:52

                          @sierdzio Thank you! It works exactly as expected.

                          Now I wonder, can I create an instance of BasicListModel dynamically? Something along the lines of :

                          function generateDynamicInstance()
                          {
                              // returns a new instance of BasicListModel
                          }
                          
                          Button {
                                onClicked:{
                                  var newInstance = generateDynamicInstance();
                                  myListView.model = newInstance;
                                }
                            
                          }
                          
                          ListView{
                              id:myListView
                          }
                          
                          S Offline
                          S Offline
                          sierdzio
                          Moderators
                          wrote on 4 Jun 2017, 14:21 last edited by
                          #12

                          @Curtwagner1984 said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                          Now I wonder, can I create an instance of BasicListModel dynamically? Something along the lines of :

                          Yes, this page in docs describes it: link. Or you can use a Loader.

                          (Z(:^

                          C 2 Replies Last reply 4 Jun 2017, 16:05
                          0
                          • S sierdzio
                            4 Jun 2017, 14:21

                            @Curtwagner1984 said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                            Now I wonder, can I create an instance of BasicListModel dynamically? Something along the lines of :

                            Yes, this page in docs describes it: link. Or you can use a Loader.

                            C Offline
                            C Offline
                            Curtwagner1984
                            wrote on 4 Jun 2017, 16:05 last edited by
                            #13

                            @sierdzio
                            I've already looked at the page Dynamic QML Object Creation from JavaScript ,but I thought the function Qt.createComponent() is only for .qml files , and the createObject object method only works on components created by the Qt.createComponent() method.

                            I've tried to create a component manually like so:

                            Component {
                                    id: myTestComponent
                                    ActorModel {
                                        id: actModel
                                        Component.onCompleted: {
                                            console.log("ACTORMODEL:ON COMPLETE TRIGGERED")
                                            init(dbManager)                
                                        }
                                    }
                                }
                            

                            and then use the createObject method on it like this myTestComponent.createObject , but it didn't complile. (Clearly, I'm doing it wrong, but I couldn't find how to use this method on a C++ registered Object in the documentation.)

                            I did manage to use a Loader like this:

                            Component {
                                    id: myTestComponent
                                    ActorModel {
                                        id: actModel
                                        Component.onCompleted: {
                                            console.log("ACTORMODEL:ON COMPLETE TRIGGERED")
                                            init(dbManager)                
                                        }
                                    }
                                }
                                    
                                Loader{
                                    id: myTestLoader
                                    sourceComponent: myTestComponent       
                                }
                            
                            
                            ListView{
                              id:myList
                              model:myTestLoader.item
                            }
                            

                            But I'm unsure as to how to trigger a creation of a new instance.

                            1 Reply Last reply
                            0
                            • S sierdzio
                              4 Jun 2017, 14:21

                              @Curtwagner1984 said in Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?:

                              Now I wonder, can I create an instance of BasicListModel dynamically? Something along the lines of :

                              Yes, this page in docs describes it: link. Or you can use a Loader.

                              C Offline
                              C Offline
                              Curtwagner1984
                              wrote on 5 Jun 2017, 09:43 last edited by
                              #14

                              @sierdzio I managed to get it to work with createObject like so:

                              Component {
                                      id: myTestComponent
                                      ActorModel {
                                          id: actModel
                                          Component.onCompleted: {
                                              console.log("ACTORMODEL:ON COMPLETE TRIGGERED")
                                              init(dbManager)                
                                          }
                                      }
                                  }
                              
                              Button {
                                onClicked: {
                                  // creates a new instance of ActorModel
                                   var x = myTestComponent.createObject();     
                                   myList.model = x;
                                }
                              }
                              
                              ListView{
                                id:myList
                              }
                              

                              Thank you very much for your help!

                              1 Reply Last reply
                              2
                              • S Offline
                                S Offline
                                sierdzio
                                Moderators
                                wrote on 6 Jun 2017, 03:56 last edited by
                                #15

                                You're welcome, happy coding :-)

                                And thanks for sharing the solution, maybe it will help somebody else, too.

                                (Z(:^

                                1 Reply Last reply
                                2

                                1/15

                                2 Jun 2017, 16:05

                                • Login

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