Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Emiting signals from singletons
Forum Updated to NodeBB v4.3 + New Features

Emiting signals from singletons

Scheduled Pinned Locked Moved Solved General and Desktop
9 Posts 3 Posters 3.9k Views 3 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.
  • D Offline
    D Offline
    deleted379
    wrote on last edited by deleted379
    #1

    Hello,

    Is it possible at all to emit signals from singleton classes that could be handled in QML?

    If yes, what is the preferred way to do so?

    Thank you in advance.

    raven-worxR 1 Reply Last reply
    0
    • D deleted379

      Hello,

      Is it possible at all to emit signals from singleton classes that could be handled in QML?

      If yes, what is the preferred way to do so?

      Thank you in advance.

      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      @stehlo said in Emiting signals from singletons:

      If yes, what is the preferred way to do so?

      the same way like from any other Item
      Why should it be different for a singleton?

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      D 1 Reply Last reply
      5
      • raven-worxR raven-worx

        @stehlo said in Emiting signals from singletons:

        If yes, what is the preferred way to do so?

        the same way like from any other Item
        Why should it be different for a singleton?

        D Offline
        D Offline
        deleted379
        wrote on last edited by
        #3

        @raven-worx said in Emiting signals from singletons:

        Why should it be different for a singleton?

        Well, I would expect it to work then, especially considering that I use the same methodology.

        Therefore, something tells me that there IS a difference.

        J.HilkJ 1 Reply Last reply
        0
        • D deleted379

          @raven-worx said in Emiting signals from singletons:

          Why should it be different for a singleton?

          Well, I would expect it to work then, especially considering that I use the same methodology.

          Therefore, something tells me that there IS a difference.

          J.HilkJ Online
          J.HilkJ Online
          J.Hilk
          Moderators
          wrote on last edited by
          #4

          @stehlo said in Emiting signals from singletons:

          Well, I would expect it to work then, especially considering that I use the same methodology.
          Therefore, something tells me that there IS a difference.

          I would beg to differ, as I'm using more than one Singelton in my Project and I have no problems with signals in QML or c++

          Can you share some code ?

          The cpp & h file of your singleton, the QML instantiation and the c++ one?


          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          D 1 Reply Last reply
          2
          • J.HilkJ J.Hilk

            @stehlo said in Emiting signals from singletons:

            Well, I would expect it to work then, especially considering that I use the same methodology.
            Therefore, something tells me that there IS a difference.

            I would beg to differ, as I'm using more than one Singelton in my Project and I have no problems with signals in QML or c++

            Can you share some code ?

            The cpp & h file of your singleton, the QML instantiation and the c++ one?

            D Offline
            D Offline
            deleted379
            wrote on last edited by
            #5

            @j-hilk said in Emiting signals from singletons:

            Can you share some code ?

            Thank you. Yes, of course.
            (Please note, they will be just simplified excerpts due to the amount of unrelated code in those files.)

            //database.h
            ...
            class Database : public QObject
            {
              Q_OBJECT
            
              explicit Database(QObject *parent = nullptr);
              Q_DISABLE_COPY(Database)
              ~Database();
            ...
              void logAndEmitCriticalError(const QString msg);
            ...
            public:
              static Database* getInstance();
              static QObject* singletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
            ...
            signals:
              void errorMessage(const QString msg);
            ...
            }
            ...
            
            //database.cpp
            ...
            Database::Database(QObject *parent) : QObject(parent)
            {
              init();
            }
            
            Database::~Database()
            {
              auto db = QSqlDatabase::database();
              if (db.isOpen()) {
                db.close();
                qInfo("Closed database connection.");
              }
            }
            ...
            Database* Database::getInstance()
            {
              static Database* inst = new Database();
              return inst;
            }
            ...
            QObject* Database::singletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
            {
              Q_UNUSED(engine)
              Q_UNUSED(scriptEngine)
              return Database::getInstance();
            }
            ...
            void Database::logAndEmitCriticalError(const QString msg)
            {
              qCritical("%s", qUtf8Printable(msg));
              emit errorMessage(msg);
            }
            ...
            
            //main.cpp
            ...
              qmlRegisterSingletonType<Database>("com.xyz.database", 1, 0,
                                                 "Database", Database::singletonTypeProvider);
            ...
            
            //Main.qml
            ...
            import com.xyz.database 1.0
            ...
              Connections {
                id: db
                target: Database
            //    onErrorMessage: {
            //      toast.show((msg.length > 0)
            //                 ? msg
            //                 : qsTr("Database: unknown error."));
            //    }
              }
            ...
            

            Now, please note:

            • I can't use onErrorMessage handler above, as I could do for non-singleton Q_OBJECTs.
            • Everything in the Database class works correctly upon initialisation, except for the emission of the signals that are handled in QML or, respectively, signals might be emitted but they aren't handled in QML.
            • Trying to fiddle with Q_PROPERTY(... NOFIFY ...) doesn't make any sense here, because there is no property to export.
            • And, naturally, I can't use the following for singleton:
              Database {
                onErrorMessage: {
                  toast.show((msg.length > 0)
                             ? msg
                             : qsTr("Database: unknown error."));
                }
              }
            

            Therefore, obviously, I am missing something – and this something is what constitutes the difference between emiting signals for non-singletons and for singletons.

            Thank you in advance.

            J.HilkJ 1 Reply Last reply
            2
            • D deleted379

              @j-hilk said in Emiting signals from singletons:

              Can you share some code ?

              Thank you. Yes, of course.
              (Please note, they will be just simplified excerpts due to the amount of unrelated code in those files.)

              //database.h
              ...
              class Database : public QObject
              {
                Q_OBJECT
              
                explicit Database(QObject *parent = nullptr);
                Q_DISABLE_COPY(Database)
                ~Database();
              ...
                void logAndEmitCriticalError(const QString msg);
              ...
              public:
                static Database* getInstance();
                static QObject* singletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
              ...
              signals:
                void errorMessage(const QString msg);
              ...
              }
              ...
              
              //database.cpp
              ...
              Database::Database(QObject *parent) : QObject(parent)
              {
                init();
              }
              
              Database::~Database()
              {
                auto db = QSqlDatabase::database();
                if (db.isOpen()) {
                  db.close();
                  qInfo("Closed database connection.");
                }
              }
              ...
              Database* Database::getInstance()
              {
                static Database* inst = new Database();
                return inst;
              }
              ...
              QObject* Database::singletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
              {
                Q_UNUSED(engine)
                Q_UNUSED(scriptEngine)
                return Database::getInstance();
              }
              ...
              void Database::logAndEmitCriticalError(const QString msg)
              {
                qCritical("%s", qUtf8Printable(msg));
                emit errorMessage(msg);
              }
              ...
              
              //main.cpp
              ...
                qmlRegisterSingletonType<Database>("com.xyz.database", 1, 0,
                                                   "Database", Database::singletonTypeProvider);
              ...
              
              //Main.qml
              ...
              import com.xyz.database 1.0
              ...
                Connections {
                  id: db
                  target: Database
              //    onErrorMessage: {
              //      toast.show((msg.length > 0)
              //                 ? msg
              //                 : qsTr("Database: unknown error."));
              //    }
                }
              ...
              

              Now, please note:

              • I can't use onErrorMessage handler above, as I could do for non-singleton Q_OBJECTs.
              • Everything in the Database class works correctly upon initialisation, except for the emission of the signals that are handled in QML or, respectively, signals might be emitted but they aren't handled in QML.
              • Trying to fiddle with Q_PROPERTY(... NOFIFY ...) doesn't make any sense here, because there is no property to export.
              • And, naturally, I can't use the following for singleton:
                Database {
                  onErrorMessage: {
                    toast.show((msg.length > 0)
                               ? msg
                               : qsTr("Database: unknown error."));
                  }
                }
              

              Therefore, obviously, I am missing something – and this something is what constitutes the difference between emiting signals for non-singletons and for singletons.

              Thank you in advance.

              J.HilkJ Online
              J.HilkJ Online
              J.Hilk
              Moderators
              wrote on last edited by J.Hilk
              #6

              First off, thanks for the detailed information. Not every poster makes the effort 😆

              @stehlo said in Emiting signals from singletons:

              Database* Database::getInstance()
              {
              static Database* inst = new Database();
              return inst;
              }
              ...
              QObject* Database::singletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
              {
              Q_UNUSED(engine)
              Q_UNUSED(scriptEngine)
              return Database::getInstance();
              }

              I'm sorry, but this is not a correct singleton pattern for Qt/QML

              this:

              Database* Database::getInstance()
              {
              static Database* inst = new Database();
              return inst;
              }
              

              is constantly creating new instances, I think you forgot the const declaration then it should work.
              (See post 8 & 10)

              here, an excerpt from one of my classes

              //.h
              private:
                  explicit DeviceInformation(QObject *parent = nullptr);
                  static QObject *DeviceInfo;
              
              public:
                  static DeviceInformation *getInstance(QObject *parent =nullptr){
                      if(DeviceInfo)
                          return  qobject_cast<DeviceInformation*>(DeviceInformation::DeviceInfo);
              
                      auto instance = new DeviceInformation(parent);
                      DeviceInfo = instance;
                      return  instance;
                  }
              
                  static QObject *instance(QQmlEngine *engine, QJSEngine *scriptEngine)
                  {
                      Q_UNUSED(scriptEngine)
              
                      if(DeviceInfo){
                          Q_UNUSED(engine)
                          return DeviceInfo;
                      }
              
                      DeviceInfo = new DeviceInformation();
                      engine->setObjectOwnership(DeviceInfo,QQmlEngine::CppOwnership);
                      return DeviceInfo;
                  }
              
              //.cpp mainly only used for the static pointer initialization
              QObject* DeviceInformation::DeviceInfo = nullptr;
              DeviceInformation::DeviceInformation(QObject *parent)
                  : QObject(parent)
              {
                  reset();
              }
              

              Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


              Q: What's that?
              A: It's blue light.
              Q: What does it do?
              A: It turns blue.

              D 1 Reply Last reply
              2
              • J.HilkJ J.Hilk

                First off, thanks for the detailed information. Not every poster makes the effort 😆

                @stehlo said in Emiting signals from singletons:

                Database* Database::getInstance()
                {
                static Database* inst = new Database();
                return inst;
                }
                ...
                QObject* Database::singletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
                {
                Q_UNUSED(engine)
                Q_UNUSED(scriptEngine)
                return Database::getInstance();
                }

                I'm sorry, but this is not a correct singleton pattern for Qt/QML

                this:

                Database* Database::getInstance()
                {
                static Database* inst = new Database();
                return inst;
                }
                

                is constantly creating new instances, I think you forgot the const declaration then it should work.
                (See post 8 & 10)

                here, an excerpt from one of my classes

                //.h
                private:
                    explicit DeviceInformation(QObject *parent = nullptr);
                    static QObject *DeviceInfo;
                
                public:
                    static DeviceInformation *getInstance(QObject *parent =nullptr){
                        if(DeviceInfo)
                            return  qobject_cast<DeviceInformation*>(DeviceInformation::DeviceInfo);
                
                        auto instance = new DeviceInformation(parent);
                        DeviceInfo = instance;
                        return  instance;
                    }
                
                    static QObject *instance(QQmlEngine *engine, QJSEngine *scriptEngine)
                    {
                        Q_UNUSED(scriptEngine)
                
                        if(DeviceInfo){
                            Q_UNUSED(engine)
                            return DeviceInfo;
                        }
                
                        DeviceInfo = new DeviceInformation();
                        engine->setObjectOwnership(DeviceInfo,QQmlEngine::CppOwnership);
                        return DeviceInfo;
                    }
                
                //.cpp mainly only used for the static pointer initialization
                QObject* DeviceInformation::DeviceInfo = nullptr;
                DeviceInformation::DeviceInformation(QObject *parent)
                    : QObject(parent)
                {
                    reset();
                }
                
                D Offline
                D Offline
                deleted379
                wrote on last edited by deleted379
                #7

                @j-hilk said in Emiting signals from singletons:

                this:
                Database* Database::getInstance()
                {
                static Database* inst = new Database();
                return inst;
                }

                is constantly creating new instances, I think you forgot the const declaration then it should work.

                Thank you for looking into this issue for me.

                However, I am very sorry but I have to disagree.

                According to The C++PL 4th Ed., page 314 (12.1.8 Local Variables) – "If a local variable is declared static, a single statically allocated object will be used to represent that variable in all calls of the function. It will be initialized only the first time a thread of execution reaches its definition."

                Thus, unless I don't understand Bjarne's explanation or Qt breaks such definition in some way, I can't see how const could make any difference here.

                On top of that, the manner of creating the singleton doesn't change the fact that the signal is not connectable in QML as demonstrated in my previous post.

                ===

                Update 1:
                I just tested the const and the error says:

                • cannot initialize return object of type 'QObject *' with an rvalue of type 'const Database *'

                The function qmlRegisterSingletonType expects a non-const QObject *.

                J.HilkJ 1 Reply Last reply
                0
                • D deleted379

                  @j-hilk said in Emiting signals from singletons:

                  this:
                  Database* Database::getInstance()
                  {
                  static Database* inst = new Database();
                  return inst;
                  }

                  is constantly creating new instances, I think you forgot the const declaration then it should work.

                  Thank you for looking into this issue for me.

                  However, I am very sorry but I have to disagree.

                  According to The C++PL 4th Ed., page 314 (12.1.8 Local Variables) – "If a local variable is declared static, a single statically allocated object will be used to represent that variable in all calls of the function. It will be initialized only the first time a thread of execution reaches its definition."

                  Thus, unless I don't understand Bjarne's explanation or Qt breaks such definition in some way, I can't see how const could make any difference here.

                  On top of that, the manner of creating the singleton doesn't change the fact that the signal is not connectable in QML as demonstrated in my previous post.

                  ===

                  Update 1:
                  I just tested the const and the error says:

                  • cannot initialize return object of type 'QObject *' with an rvalue of type 'const Database *'

                  The function qmlRegisterSingletonType expects a non-const QObject *.

                  J.HilkJ Online
                  J.HilkJ Online
                  J.Hilk
                  Moderators
                  wrote on last edited by
                  #8

                  @stehlo
                  ok,

                  here's a minimal working example

                  #ifndef MYSINGLETON_H
                  #define MYSINGLETON_H
                  
                  #include <QObject>
                  #include <QTimer>
                  #include <QQmlEngine>
                  
                  class mySingleton : public QObject
                  {
                      Q_OBJECT
                  
                  private:
                      explicit mySingleton(QObject *parent = nullptr);
                      static QObject *MySingleton;
                  
                  public:
                      static mySingleton *getInstance(QObject *parent =nullptr){
                          if(MySingleton)
                              return  qobject_cast<mySingleton*>(mySingleton::MySingleton);
                  
                          auto instance = new mySingleton(parent);
                          MySingleton = instance;
                          return  instance;
                      }
                  
                      static QObject *instance(QQmlEngine *engine, QJSEngine *scriptEngine)
                      {
                          Q_UNUSED(scriptEngine)
                  
                          if(MySingleton){
                              Q_UNUSED(engine)
                              return MySingleton;
                          }
                  
                          MySingleton = new mySingleton();
                          engine->setObjectOwnership(MySingleton,QQmlEngine::CppOwnership);
                          return MySingleton;
                      }
                  
                  signals:
                      void errorMessage(const QString &str);
                  };
                  
                  #endif // MYSINGLETON_H
                  
                  
                  #include "mysingleton.h"
                  
                  #include <QTimer>
                  #include <QTime>
                  
                  static const QString newError("new Error %1");
                  
                  QObject* mySingleton::MySingleton = nullptr;
                  mySingleton::mySingleton(QObject *parent) : QObject(parent)
                  {
                      QTimer *t(new QTimer(this));
                      connect(t, &QTimer::timeout, this, [=]()->void{
                          emit errorMessage(newError.arg(QTime::currentTime().toString()));
                      });
                  
                      t->start(1000);
                  }
                  
                  
                  #include <QApplication>
                  #include <QQmlApplicationEngine>
                  
                  #include "mysingleton.h"
                  #include "QQmlContext"
                  #include <QDebug>
                  
                  int main(int argc, char *argv[])
                  {
                      QApplication app(argc, argv);
                  
                      qmlRegisterSingletonType<mySingleton>("MySingleton", 1, 0, "Singel", mySingleton::instance);
                  
                      mySingleton *cppSingelton = mySingleton::getInstance();
                  
                      QQmlApplicationEngine engine;
                      QObject::connect(cppSingelton, &mySingleton::errorMessage, &engine, [](const QString error)->void{
                          qDebug() << "Cpp Slot" << error;
                      });
                  
                  
                      engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                      if (engine.rootObjects().isEmpty())
                          return -1;
                  
                      return app.exec();
                  }
                  
                  
                  import QtQuick 2.12
                  import QtQuick.Window 2.12
                  
                  import MySingleton 1.0
                  
                  Window {
                      visible: true
                      width: 400
                      height: 750
                      title: qsTr("Hello World")
                  
                      Connections{
                          target: Singel
                  
                          onErrorMessage: console.log("QML slot", str)
                      }
                  }
                  

                  Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                  Q: What's that?
                  A: It's blue light.
                  Q: What does it do?
                  A: It turns blue.

                  D 1 Reply Last reply
                  5
                  • J.HilkJ J.Hilk

                    @stehlo
                    ok,

                    here's a minimal working example

                    #ifndef MYSINGLETON_H
                    #define MYSINGLETON_H
                    
                    #include <QObject>
                    #include <QTimer>
                    #include <QQmlEngine>
                    
                    class mySingleton : public QObject
                    {
                        Q_OBJECT
                    
                    private:
                        explicit mySingleton(QObject *parent = nullptr);
                        static QObject *MySingleton;
                    
                    public:
                        static mySingleton *getInstance(QObject *parent =nullptr){
                            if(MySingleton)
                                return  qobject_cast<mySingleton*>(mySingleton::MySingleton);
                    
                            auto instance = new mySingleton(parent);
                            MySingleton = instance;
                            return  instance;
                        }
                    
                        static QObject *instance(QQmlEngine *engine, QJSEngine *scriptEngine)
                        {
                            Q_UNUSED(scriptEngine)
                    
                            if(MySingleton){
                                Q_UNUSED(engine)
                                return MySingleton;
                            }
                    
                            MySingleton = new mySingleton();
                            engine->setObjectOwnership(MySingleton,QQmlEngine::CppOwnership);
                            return MySingleton;
                        }
                    
                    signals:
                        void errorMessage(const QString &str);
                    };
                    
                    #endif // MYSINGLETON_H
                    
                    
                    #include "mysingleton.h"
                    
                    #include <QTimer>
                    #include <QTime>
                    
                    static const QString newError("new Error %1");
                    
                    QObject* mySingleton::MySingleton = nullptr;
                    mySingleton::mySingleton(QObject *parent) : QObject(parent)
                    {
                        QTimer *t(new QTimer(this));
                        connect(t, &QTimer::timeout, this, [=]()->void{
                            emit errorMessage(newError.arg(QTime::currentTime().toString()));
                        });
                    
                        t->start(1000);
                    }
                    
                    
                    #include <QApplication>
                    #include <QQmlApplicationEngine>
                    
                    #include "mysingleton.h"
                    #include "QQmlContext"
                    #include <QDebug>
                    
                    int main(int argc, char *argv[])
                    {
                        QApplication app(argc, argv);
                    
                        qmlRegisterSingletonType<mySingleton>("MySingleton", 1, 0, "Singel", mySingleton::instance);
                    
                        mySingleton *cppSingelton = mySingleton::getInstance();
                    
                        QQmlApplicationEngine engine;
                        QObject::connect(cppSingelton, &mySingleton::errorMessage, &engine, [](const QString error)->void{
                            qDebug() << "Cpp Slot" << error;
                        });
                    
                    
                        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                        if (engine.rootObjects().isEmpty())
                            return -1;
                    
                        return app.exec();
                    }
                    
                    
                    import QtQuick 2.12
                    import QtQuick.Window 2.12
                    
                    import MySingleton 1.0
                    
                    Window {
                        visible: true
                        width: 400
                        height: 750
                        title: qsTr("Hello World")
                    
                        Connections{
                            target: Singel
                    
                            onErrorMessage: console.log("QML slot", str)
                        }
                    }
                    
                    D Offline
                    D Offline
                    deleted379
                    wrote on last edited by
                    #9

                    @j-hilk said in Emiting signals from singletons:

                    here's a minimal working example

                    Thank you so much. Your kind help is greatly appreciated.

                    I have carefully examined the construction of all the relevant parts and ported the necessary ones into my code base.

                    I can confirm that everything works well now.

                    I have also observed your use of (A) passing by reference of constants and (B) setting the object's ownership [to C++]. I have used these to improve the quality of my code. In respect to the latter, I have read up on the topic yesterday and I was therefore glad to see you using it today, which confirmed the notion that it would be a good idea to use it eventually.

                    On top of that, I have noticed during today's testing that many signal emissions were made before the ApplicationWindow was completed and this was the reason for non-handling of those signals in QML.

                    1 Reply Last reply
                    3

                    • Login

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