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