Ok. I finally manged singletons in QML and C++ in Qt 6.7 which could be a bit frustrating for newbies like me especialy when you are reading official Qt documentation which is reffering to another doc and another doc and you finally end with 25 pages opened in your browser. I'm writting it here in case if someone got there via google search or something. Important thing about singletons that you don't call them by id property (id doesn't matter if you set it) but class name and it must start from upper letter
QML singleton global object
So. If you want to have global QML object like DataModule.qml which is created just like that and have access to it from anywhere:
Create your DataModule.qml file (first upper character is important). For example:
pragma Singleton
import QtQuick
QtObject {
property string someString: "foo bar"
}
Notice: pragma Singleton, it is important
2. In CMakeLists.txt add this:
set_source_files_properties(DataModule.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
qt_add_qml_module(appMyApp
URI MyApp
VERSION 1.0
QML_FILES
QML_FILES DataModule.qml
.......
)
Notice: Make sure that set_source_files_properties is called before qt_add_qml_module. Also, not sure if it matter, but add QML_FILES DataModule.qml as first or at least before your Main.qml
3. Now you can call your someString property from anywhere without declaring class in ApplicationWindow {}. For example, just reffer to class DataModule directly:
ApplicationWindow {
id: mainWindow
width: 640
height: 480
visible: true
Component.onCompleted: {
console.log(DataModule.someString);
}
}
QObject/C++ singleton global object
If you want to have global QObject/C++ access from anywhere in your QML without rootContext()->setContextProperty
Create your QObject class and save it and add to project for example
class MyCppInterface : public QObject
{
Q_OBJECT
QML_ELEMENT <--- important
QML_SINGLETON <--- important
public:
explicit MyCppInterface(QObject *parent = nullptr);
Q_INVOKABLE void log(QString s) const;
signals:
};
Notice: You have to add both, QML_ELEMENT and QML_SINGLETON
2. Now, you DON'T NEED to declare class in ApplicationWindow to have acces to it by id like you normally did in simple QML_ELEMENT:
ApplicationWindow {
id: mainWindow
width: 640
height: 480
visible: true
MyCppInterface {
id: myCppIntf
}
Component.onCompleted: {
myCppIntf.log('something');
}
}
Instead. Just call it directly by class (first character is upper!)
ApplicationWindow {
id: mainWindow
width: 640
height: 480
visible: true
Component.onCompleted: {
MyCppInterface.log('something');
}
}
Mixing both together
Now. When we know how bot things work, we can make it mixed and have put together in one place. DataModule.qml:
pragma Singleton
import QtQuick
QtObject {
property string someString: "foo bar"
function log(l) {
MyCppInterface.log(l);
}
}
And call it from anywhere:
ApplicationWindow {
id: mainWindow
width: 640
height: 480
visible: true
Component.onCompleted: {
DataModule.log("test log");
}
}
Ideally, I would like to hide global access to MyCppInterface and have it only via DataModule. This could by probably possible via Qt.createQmlObject("MyCppInterface") or Qt.createComponent("MyCppInterface") or something like that inside DataModule {} but I'm very happy what I obtained so far and I like it :)