Cannot call C++ method when object is created in another QML module
-
Hi,
I have this QObject class:class CHPCppInterface : public QObject { Q_OBJECT QML_ELEMENT public: explicit CHPCppInterface(QObject *parent = nullptr); //int tescik; const; Q_INVOKABLE bool saveAccessToken(QString token) const; Q_INVOKABLE QString getAccessgetToken() const; signals: void tescikUpdated(); };
When I declare this class in main Window then it works:
Window { id: mainWindow width: 640 height: 480 CHPCppInterface { id: test } ........ RoundButton { onClicked: { test.saveAccessToken("test"); } }
I like to have main data module where I have common and often used thinks. So I made
DataModule.qml
and moved this class:import QtQuick Item { id: dataModule CHPCppInterface { id: test } }
And now when I try to call it:
RoundButton { onClicked: { DataModule.test.saveAccessToken("test"); } }
... I'm getting
TypeError: Cannot call method 'saveAccessToken' of undefined
How to solve that?Another annoying things:
- Even in working scenario, Qt Creator is showing red circle on that class with info "unknown component". How to fix that?
- Why in module I have to declare this class in Item{} ? Why I can't declare it directly like this:
import QtQuick CHPCppInterface { id: test }
I'm getting syntax error
-
have you tried to use QML_SINGLETON and to create the class from C++ ?
https://t1p.de/ekkeQML_SINGLETONor if you want to control the lifecycle from QML use QT_QML_SINGLETON_TYPE
https://doc.qt.io/qt-6/qml-singleton.html -
I knew both of your solutions but I thought that it is possible to fix that with QML_ELEMENT
@ekkescorner QML_SINGLETON was to much effort for me since I'm not experienced with C++ macros described on your site. Even Qt doc about QML_ELEMENT is too much complicated with my experience and I think whole idea is form over substance for my simple object
@Ronel_qtmaster I decided for this solution, works fine so far and "uknown component" marker disappeared from my QML file.Thank you both guys
-
-
-
BTW: My problem was probably not understaing QML objects life cycle and creation. Creating
DataModule.qml
file and declaring:import QtQuick Item { id: dataModule CHPCppInterface { id: test } }
... doesn't mean that
DataModule{}
object instance is created. Hence my problem with access to this object. I guess that I must makeDataModule
as global component, not only as subcomponent of main window -
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. InCMakeLists.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 beforeqt_add_qml_module
. Also, not sure if it matter, but addQML_FILES DataModule.qml
as first or at least before yourMain.qml
3. Now you can call yoursomeString
property from anywhere without declaring class inApplicationWindow {}
. 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 withoutrootContext()->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
andQML_SINGLETON
2. Now, you DON'T NEED to declare class in ApplicationWindow to have acces to it byid
like you normally did in simpleQML_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 viaDataModule
. This could by probably possible viaQt.createQmlObject("MyCppInterface")
orQt.createComponent("MyCppInterface")
or something like that insideDataModule {}
but I'm very happy what I obtained so far and I like it :)