3

I have a class SingletonBaseClass that is defined in C++ and later extended in QML. In the qmldir file, it is marked as a Singleton:

// qmldir file
singleton SingletonClass 1.0 SingletonClass.qml

// SingletonClass.qml
import QtQml 2.0
pragma Singleton
SingletonBaseClass {
    // ...
}

This is the base class:

class SingletonBaseClass : public QObject 
{
    Q_OBJECT
    public:
    SingletonBaseClass(QObject* parent = nullptr) {}

    // Get the owner engine
    Q_INVOKABLE void someMethodCalledFromQml()
    {
        QQmlEngine* ownerEngine = ?
    }

    // Get the single instance of the given engine
    static SingletonBaseClass* instance(QQmlEngine* engine)
    {
        SingletonBaseClass* instance = ?
        return instance;
    } 

};
  1. How can I retrieve the QQmlEngine instance in SingletonBaseClass?
  2. How can I get a pointer to my singleton instance from within a static function?

Note: I have multiple QML engine instances in my application.

[Update]: I wasn't satisfied with the suggested workarounds and so I finally contributed this patch for 1. and QQmlEngine::singletonInstance() for 2. Both changes will be available in Qt 5.12.

Richard W
  • 631
  • 4
  • 15

1 Answers1

5

If you want to get the QQmlEngine you can use the contextForObject() method as I show below:

Q_INVOKABLE void someMethodCalledFromQml()
{
    QQmlEngine *ownerEngine = QQmlEngine::contextForObject(this)->engine();
    qDebug()<<ownerEngine;
}

singletons in qml do not have a parent so a way to access them is by creating a property, for that we create a .qml and create an object with that property as the Singleton:

qrc:/tmp.qml

import QtQuick 2.0

QtObject {
    property var obj: SingletonClass
}

and then this element is accessed by the following code:

static SingletonBaseClass* instance(QQmlEngine* engine)
{
    QQmlComponent component(engine, QUrl("qrc:/tmp.qml"));
    QObject *item = component.create();
    SingletonBaseClass *instance = qvariant_cast<SingletonBaseClass *>(item->property("obj"));
    return instance;
}

In the following link we show an example.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks for taking the time and the extensive answer. First part, [the documentation says](http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType-1) that contextForObject() is supposed to return NULL for singleton types. Do you have an idea why it still works in that case? Maybe because it is instantiated as QML component and not using a singleton provider function? Second part: nice trick! I tried to avoid the additional file by putting the content into a QQmlExpression, but that doesn't work. Maybe because of [QTBUG-33514](https://bugreports.qt.io/browse/QTBUG-33514). – Richard W Jan 14 '18 at 13:10
  • From your link, I do not see contextForObject() returning NULL for singleton types. If the singleton is instantiated by QML, it owns the singleton. However, C++ code can instantiate the singleton prior to reference by QML. Perhaps a singleton instantiated in C++ with C++ ownership will return nullptr because it's owner is not QML. On a side note, If instantiated in C++, call QQmlEngine::setObjectOwnership() to prevent QML from deleting it or your app will crash intermittently. Once referenced in QML, QML takes ownership and will delete it at runtime under the proper circumstances. – pixelgrease Oct 28 '20 at 00:49