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. Dependency injection in C++ class registered with QML_SINGLETON

Dependency injection in C++ class registered with QML_SINGLETON

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
c++qml qmlsingleton
13 Posts 6 Posters 2.2k 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.
  • E Offline
    E Offline
    ECEC
    wrote on 1 Dec 2023, 16:33 last edited by
    #1

    According to https://doc.qt.io/qt-6/qtqml-cppintegration-contextproperties.html, context properties should be avoided in favour of Singletons.

    I have three C++ classes registered with QML_SINGLETON. These are SettingsManager, ControlPanel, and DesignManager, with each class being responsible for a separate area of the UI. All three classes need 2 pointers to 2 instances of a NetworkProcessor class. If I were doing this the old way with context properties, I would simply pass the pointers in each class' constructor, thereby enforcing a coupling. As that isn't an option with the singleton pattern, I see no other option than to set these pointers after the singletons have been instantiated, retrieving the instance via a static instance() function of sorts.

    Is this an acceptable way of injecting dependencies when working with singletons or is there a better way? I feel uneasy that the singletons could be used before their dependencies have been injected.

    T 1 Reply Last reply 10 Mar 2024, 02:25
    0
    • E ECEC
      1 Dec 2023, 16:33

      According to https://doc.qt.io/qt-6/qtqml-cppintegration-contextproperties.html, context properties should be avoided in favour of Singletons.

      I have three C++ classes registered with QML_SINGLETON. These are SettingsManager, ControlPanel, and DesignManager, with each class being responsible for a separate area of the UI. All three classes need 2 pointers to 2 instances of a NetworkProcessor class. If I were doing this the old way with context properties, I would simply pass the pointers in each class' constructor, thereby enforcing a coupling. As that isn't an option with the singleton pattern, I see no other option than to set these pointers after the singletons have been instantiated, retrieving the instance via a static instance() function of sorts.

      Is this an acceptable way of injecting dependencies when working with singletons or is there a better way? I feel uneasy that the singletons could be used before their dependencies have been injected.

      T Offline
      T Offline
      talksik
      wrote on 10 Mar 2024, 02:25 last edited by talksik 3 Oct 2024, 02:28
      #2

      @ECEC were you able to figure this out? I have a similar dilemma. However, I have model classes declared in c++ with the QML_ELEMENT only. I use this in my qml, but I want to pass in a “repository” or data manager class that is in c++ and would handle data fetching and a websocket connection. This data manager class has the QML_SINGLETON macro.

      On initialization of the various models in qml (ex: AuthModel {} or UserProfileModel {}) I have no way of providing it the necessary dependency of the data manager singleton. As a workaround, I can have connections in qml that manages signals and slots between the data manager singleton in qml and the models that were initialized, but it feels really off and spaghetti because qml layer shouldnt need to handle model to data layer communication. The model in c++ should be able to abstract the complexity.

      By the way, I need data manager instance to be shared instance instead of each model initializing it themselves because I only want one websocket connection.

      I find the qml and c++ integration as messy.

      Any ideas?

      T 1 Reply Last reply 10 Mar 2024, 17:15
      0
      • T talksik
        10 Mar 2024, 02:25

        @ECEC were you able to figure this out? I have a similar dilemma. However, I have model classes declared in c++ with the QML_ELEMENT only. I use this in my qml, but I want to pass in a “repository” or data manager class that is in c++ and would handle data fetching and a websocket connection. This data manager class has the QML_SINGLETON macro.

        On initialization of the various models in qml (ex: AuthModel {} or UserProfileModel {}) I have no way of providing it the necessary dependency of the data manager singleton. As a workaround, I can have connections in qml that manages signals and slots between the data manager singleton in qml and the models that were initialized, but it feels really off and spaghetti because qml layer shouldnt need to handle model to data layer communication. The model in c++ should be able to abstract the complexity.

        By the way, I need data manager instance to be shared instance instead of each model initializing it themselves because I only want one websocket connection.

        I find the qml and c++ integration as messy.

        Any ideas?

        T Offline
        T Offline
        talksik
        wrote on 10 Mar 2024, 17:15 last edited by talksik 3 Oct 2024, 17:18
        #3

        I managed to find a way to enable the dependency injection pattern:

        // contactsModel.h (SEE the `MessengerService*` property)
        
        class ContactsModel: public QAbstractListModel
        {
            Q_OBJECT
            QML_ELEMENT
            Q_PROPERTY(MessengerService* messengerService READ messengerService WRITE setMessengerService NOTIFY messengerServiceChanged REQUIRED)
        
        public:
            enum ContactRoles
            {
                DisplayName = Qt::UserRole + 1,
                Bio,
                ProfilePictureUrl,
                Email,
                Tuned,
                Status
            };
        
            ContactsModel(QObject *parent = nullptr);
        
            int rowCount(const QModelIndex &parent = QModelIndex()) const;
            QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
        
            MessengerService* messengerService() const;
            void setMessengerService(MessengerService* _messengerService);
        
        signals:
            void messengerServiceChanged();
        
        public slots:
            // fetches contacts from api
            void fetchContacts();
            // update whether or not we are tuned into this person
            void updateTuneContact(const QString &email, const bool tuneIn = false);
        
        private slots:
            void finishedFetchContacts();
            void finishedUpdateTuneContact();
        
        protected:
            QHash<int, QByteArray> roleNames() const;
        
        private:
            QList<Contact> m_contacts;
        
            QNetworkAccessManager *m_net_access_manager;
            QNetworkReply *m_net_contacts_reply;
            QNetworkReply *m_net_update_tuned_contact;
        
            MessengerService* m_messenger_service;
        };
        
        // messengerservice.h (This is what I want as a shared dependency between different models)
        // in charge of managing connection with messenger service
        // realtime & presence messages
        class MessengerService: public QObject
        {
            Q_OBJECT
            QML_ELEMENT
            QML_SINGLETON
        public:
            MessengerService() = default;
        
        signals:
            // filter for presence only messages
            // these would be pongs of presence heartbeats to other clients
            void incomingPresencePong(const IncomingPresencePong &message);
            void incomingPresenceQuery(const IncomingPresenceQuery &message);
        
            // all messages
            void messageReceived(const QString &message);
        
        public slots:
            void connect(const QString &email);
        
            // successfully connected
            void connected();
            void disconnected();
            void errorOccurred(QAbstractSocket::SocketError err);
        
            void sendTextMessage(const QString &message);
        
            void sendPresencePong(const OutgoingPresencePong &pong);
            void sendPresenceQuery(const OutgoingPresenceQuery &query);
        
        private slots:
            void textMessageReceived(const QString &message);
        
        private:
            QWebSocket m_webSocket;
        };
        }
        
        // Now in our QML
            ContactsModel {
                id: contactsModel
                messengerService: MessengerService
            }
        

        Now, the compiler will require the ContactsModel initialization to include an instance of MessengerService. Since MessengerService is registered as a QML_SINGLETON, it can be passed in.

        We can then also pass the same MessengerService singleton to other models that are initialized in QML.

        @SGaist any thoughts on this approach of mine? My experience with golang backend design patterns and also flutter bloc pattern inspired me to do it this way.

        Do you know if there is an enterprise standard patterns for Qt/QML apps? I am shocked that Qt doesn't document how complex apps should be written (where there are many "view models" needing to access each other.

        B 1 Reply Last reply 10 Mar 2024, 22:29
        1
        • T talksik
          10 Mar 2024, 17:15

          I managed to find a way to enable the dependency injection pattern:

          // contactsModel.h (SEE the `MessengerService*` property)
          
          class ContactsModel: public QAbstractListModel
          {
              Q_OBJECT
              QML_ELEMENT
              Q_PROPERTY(MessengerService* messengerService READ messengerService WRITE setMessengerService NOTIFY messengerServiceChanged REQUIRED)
          
          public:
              enum ContactRoles
              {
                  DisplayName = Qt::UserRole + 1,
                  Bio,
                  ProfilePictureUrl,
                  Email,
                  Tuned,
                  Status
              };
          
              ContactsModel(QObject *parent = nullptr);
          
              int rowCount(const QModelIndex &parent = QModelIndex()) const;
              QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
          
              MessengerService* messengerService() const;
              void setMessengerService(MessengerService* _messengerService);
          
          signals:
              void messengerServiceChanged();
          
          public slots:
              // fetches contacts from api
              void fetchContacts();
              // update whether or not we are tuned into this person
              void updateTuneContact(const QString &email, const bool tuneIn = false);
          
          private slots:
              void finishedFetchContacts();
              void finishedUpdateTuneContact();
          
          protected:
              QHash<int, QByteArray> roleNames() const;
          
          private:
              QList<Contact> m_contacts;
          
              QNetworkAccessManager *m_net_access_manager;
              QNetworkReply *m_net_contacts_reply;
              QNetworkReply *m_net_update_tuned_contact;
          
              MessengerService* m_messenger_service;
          };
          
          // messengerservice.h (This is what I want as a shared dependency between different models)
          // in charge of managing connection with messenger service
          // realtime & presence messages
          class MessengerService: public QObject
          {
              Q_OBJECT
              QML_ELEMENT
              QML_SINGLETON
          public:
              MessengerService() = default;
          
          signals:
              // filter for presence only messages
              // these would be pongs of presence heartbeats to other clients
              void incomingPresencePong(const IncomingPresencePong &message);
              void incomingPresenceQuery(const IncomingPresenceQuery &message);
          
              // all messages
              void messageReceived(const QString &message);
          
          public slots:
              void connect(const QString &email);
          
              // successfully connected
              void connected();
              void disconnected();
              void errorOccurred(QAbstractSocket::SocketError err);
          
              void sendTextMessage(const QString &message);
          
              void sendPresencePong(const OutgoingPresencePong &pong);
              void sendPresenceQuery(const OutgoingPresenceQuery &query);
          
          private slots:
              void textMessageReceived(const QString &message);
          
          private:
              QWebSocket m_webSocket;
          };
          }
          
          // Now in our QML
              ContactsModel {
                  id: contactsModel
                  messengerService: MessengerService
              }
          

          Now, the compiler will require the ContactsModel initialization to include an instance of MessengerService. Since MessengerService is registered as a QML_SINGLETON, it can be passed in.

          We can then also pass the same MessengerService singleton to other models that are initialized in QML.

          @SGaist any thoughts on this approach of mine? My experience with golang backend design patterns and also flutter bloc pattern inspired me to do it this way.

          Do you know if there is an enterprise standard patterns for Qt/QML apps? I am shocked that Qt doesn't document how complex apps should be written (where there are many "view models" needing to access each other.

          B Offline
          B Offline
          Bob64
          wrote on 10 Mar 2024, 22:29 last edited by
          #4

          @talksik I had a similar reaction to the advice to move away from context properties to singletons. However, it was pointed out to me that it wasn't necessary for the C++ object actually to be a singleton.

          The advice was to use qmlRegisterSingletonInstance, whereby an object is registered as the instance that is accessed from QML. The object just needs to be a QObject and provide a QML-compatible API, but how it is created, what constructor arguments it has, etc., is up to you. The "singleton" aspect simply refers to the syntax with which it is accessed from QML.

          T 1 Reply Last reply 11 Mar 2024, 13:46
          1
          • B Bob64
            10 Mar 2024, 22:29

            @talksik I had a similar reaction to the advice to move away from context properties to singletons. However, it was pointed out to me that it wasn't necessary for the C++ object actually to be a singleton.

            The advice was to use qmlRegisterSingletonInstance, whereby an object is registered as the instance that is accessed from QML. The object just needs to be a QObject and provide a QML-compatible API, but how it is created, what constructor arguments it has, etc., is up to you. The "singleton" aspect simply refers to the syntax with which it is accessed from QML.

            T Offline
            T Offline
            talksik
            wrote on 11 Mar 2024, 13:46 last edited by talksik 3 Nov 2024, 13:48
            #5

            @Bob64 Thank you Bob for the suggestion!

            I just want to clarify something. In the qml code I provided I am initializing ContactsModel {}. However, what is not shown is that this model is initialized is a "sub-qml" file called Dashboard.qml. My main qml file looks like this:

            ApplicationWindow {
                id: root
                visible: true
            
                ...
            
                // Container element for rotating screen
                Rectangle {
                    id: main
                    ...
                    StackView {
                        id: stackView
                        anchors.fill: parent
                        initialItem: loginView
            
                        Component {
                            id: loginView
                            Login {}
                        }
                        Component {
                            id: dashboardView
                            Dashboard {}
                        }
                    }
                }
            }
            

            Note the dashboardView component. I have it this way because I do not want ContactsModel to be a top-level model. I want it to have the lifecycle of the dashboardView.

            From what you said, I imagine you are suggesting something similar to the following?

            // main.cpp
            MessengerService* messengerService = new MessengerService();
            
            ContactsModel* contactsModel = new ContactsModel(messengerService);
            
            qmlRegisterSingletonInstance(contactsModel, ...);
            

            What I am trying to achieve is the following: whenever we want to use certain models in any qml file, it should receive the MessengerService instance on initialization. Right now I am doing a hacky way by making messengerService a Q_PROPERTY that gets set in the QML where I initialize one ContactsModel {}.

            Thank you! Please let me know if that makes sense. I would appreciate any tips :)

            B 1 Reply Last reply 11 Mar 2024, 14:39
            0
            • T talksik
              11 Mar 2024, 13:46

              @Bob64 Thank you Bob for the suggestion!

              I just want to clarify something. In the qml code I provided I am initializing ContactsModel {}. However, what is not shown is that this model is initialized is a "sub-qml" file called Dashboard.qml. My main qml file looks like this:

              ApplicationWindow {
                  id: root
                  visible: true
              
                  ...
              
                  // Container element for rotating screen
                  Rectangle {
                      id: main
                      ...
                      StackView {
                          id: stackView
                          anchors.fill: parent
                          initialItem: loginView
              
                          Component {
                              id: loginView
                              Login {}
                          }
                          Component {
                              id: dashboardView
                              Dashboard {}
                          }
                      }
                  }
              }
              

              Note the dashboardView component. I have it this way because I do not want ContactsModel to be a top-level model. I want it to have the lifecycle of the dashboardView.

              From what you said, I imagine you are suggesting something similar to the following?

              // main.cpp
              MessengerService* messengerService = new MessengerService();
              
              ContactsModel* contactsModel = new ContactsModel(messengerService);
              
              qmlRegisterSingletonInstance(contactsModel, ...);
              

              What I am trying to achieve is the following: whenever we want to use certain models in any qml file, it should receive the MessengerService instance on initialization. Right now I am doing a hacky way by making messengerService a Q_PROPERTY that gets set in the QML where I initialize one ContactsModel {}.

              Thank you! Please let me know if that makes sense. I would appreciate any tips :)

              B Offline
              B Offline
              Bob64
              wrote on 11 Mar 2024, 14:39 last edited by
              #6

              @talksik OK, I see what you are saying. I think what you are doing is probably reasonable in that case, but somebody more expert than me might be able to make other suggestions.

              T 1 Reply Last reply 12 Mar 2024, 00:51
              1
              • B Bob64
                11 Mar 2024, 14:39

                @talksik OK, I see what you are saying. I think what you are doing is probably reasonable in that case, but somebody more expert than me might be able to make other suggestions.

                T Offline
                T Offline
                talksik
                wrote on 12 Mar 2024, 00:51 last edited by
                #7

                @Bob64 thank you!

                @SGaist Any thoughts? Appreciate you!

                SGaistS 1 Reply Last reply 14 Mar 2024, 20:21
                0
                • T talksik
                  12 Mar 2024, 00:51

                  @Bob64 thank you!

                  @SGaist Any thoughts? Appreciate you!

                  SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 14 Mar 2024, 20:21 last edited by
                  #8

                  @talksik Something is not exactly clear from your explanations.

                  Can you explain your architecture ? You seem to have several models that are each in charge of a different aspect of your MessengerService, is that correct ?

                  If so, it seems that you should basically create all your models passing the service to them and then set them as qmlRegisterSingletonInstance as suggested by @Bob64. I would no see any advantages creating the MessengerService in QML to pass it to your model unless you plan to be able to switch MessengerService objects on the fly. This would require for your models to have a suitable API to do the change.

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

                  T 1 Reply Last reply 14 Mar 2024, 21:48
                  0
                  • SGaistS SGaist
                    14 Mar 2024, 20:21

                    @talksik Something is not exactly clear from your explanations.

                    Can you explain your architecture ? You seem to have several models that are each in charge of a different aspect of your MessengerService, is that correct ?

                    If so, it seems that you should basically create all your models passing the service to them and then set them as qmlRegisterSingletonInstance as suggested by @Bob64. I would no see any advantages creating the MessengerService in QML to pass it to your model unless you plan to be able to switch MessengerService objects on the fly. This would require for your models to have a suitable API to do the change.

                    T Offline
                    T Offline
                    talksik
                    wrote on 14 Mar 2024, 21:48 last edited by talksik
                    #9

                    @SGaist Thank you so much for the response!

                    I considered that approach as @Bob64 suggested, but the thing is: I initialize the model within a sub-qml file deep within the QML tree. I also do NOT do the qmlRegisterSingletonInstance approach because I want the model to have the same lifecycle as the QML component that it is initialized in. Even more, this approach allow multiple of the same model being created within the QML in different places/pages/views.

                    The qmlRegisterSingletonInstance registers a singleton, which is not what I would like in my architecture. I may do that if that's the only way, however, I wanted to see if there was a way to do it my way so that each model is isolated to the QML component (and sub-components) that uses it.

                    You are right that I do not want to switch MessengerService objects on the fly. I am stuck in this dilemma. Please see my response to @Bob64's suggestion here for clarity: https://forum.qt.io/post/792823

                    T 1 Reply Last reply 15 Mar 2024, 18:04
                    0
                    • T talksik
                      14 Mar 2024, 21:48

                      @SGaist Thank you so much for the response!

                      I considered that approach as @Bob64 suggested, but the thing is: I initialize the model within a sub-qml file deep within the QML tree. I also do NOT do the qmlRegisterSingletonInstance approach because I want the model to have the same lifecycle as the QML component that it is initialized in. Even more, this approach allow multiple of the same model being created within the QML in different places/pages/views.

                      The qmlRegisterSingletonInstance registers a singleton, which is not what I would like in my architecture. I may do that if that's the only way, however, I wanted to see if there was a way to do it my way so that each model is isolated to the QML component (and sub-components) that uses it.

                      You are right that I do not want to switch MessengerService objects on the fly. I am stuck in this dilemma. Please see my response to @Bob64's suggestion here for clarity: https://forum.qt.io/post/792823

                      T Offline
                      T Offline
                      talksik
                      wrote on 15 Mar 2024, 18:04 last edited by
                      #10

                      @sierdzio Any ideas on this? Thank you!!! :)

                      T 1 Reply Last reply 16 Mar 2024, 05:26
                      0
                      • T talksik
                        15 Mar 2024, 18:04

                        @sierdzio Any ideas on this? Thank you!!! :)

                        T Offline
                        T Offline
                        talksik
                        wrote on 16 Mar 2024, 05:26 last edited by talksik
                        #11

                        @KH-219Design I found your great details of using MVVM with Qt/QML! I agree with it completely (https://forum.qt.io/topic/127714/qt-qml-c-hybrid-application-best-practices/10) Can you please help me in the dilemma I present in this thread?

                        What if we have a ViewModel that we define in C++, but that view model and other view models want to share a data object that is a singleton. How would you pass that singleton. I instantiate my view models that were defined in C++ within my QML deep within the QML tree. I see that you don't have this problem because you send the view models to QML using context properties. Please read the comments before this to understand what I am saying.

                        I would really appreciate this as I am shocked that I still haven't found an elegant way around this :)

                        E 1 Reply Last reply 10 Aug 2024, 06:50
                        0
                        • T talksik
                          16 Mar 2024, 05:26

                          @KH-219Design I found your great details of using MVVM with Qt/QML! I agree with it completely (https://forum.qt.io/topic/127714/qt-qml-c-hybrid-application-best-practices/10) Can you please help me in the dilemma I present in this thread?

                          What if we have a ViewModel that we define in C++, but that view model and other view models want to share a data object that is a singleton. How would you pass that singleton. I instantiate my view models that were defined in C++ within my QML deep within the QML tree. I see that you don't have this problem because you send the view models to QML using context properties. Please read the comments before this to understand what I am saying.

                          I would really appreciate this as I am shocked that I still haven't found an elegant way around this :)

                          E Offline
                          E Offline
                          eternal_void
                          wrote on 10 Aug 2024, 06:50 last edited by
                          #12

                          @talksik Have you found an elegant way around this? I'm struggling to find a pretty way of sharing single data source object among multiple models in QML.

                          This object communicates with QSerialPort so I guess it can't be anything but a singleton. Currently I'm using qmlRegisterSingletonInstance for all of my models which I create in main.cpp passing the data source object pointer to their constructors. It looks sort of messy if you ask me.

                          So, any ideas? Thank you!

                          1 Reply Last reply
                          0
                          • A Offline
                            A Offline
                            ajpat
                            wrote on 24 Feb 2025, 22:46 last edited by
                            #13

                            Hey @eternal_void , I just got back to using Qt for another project. This is new account (I was @talksik), but here are my findings in a similar post after a ton of research and coming up with a custom solution not really mentioned anywhere: https://forum.qt.io/post/821291

                            1 Reply Last reply
                            1
                            • B Bob64 referenced this topic on 29 Mar 2025, 11:51

                            • Login

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