Access to an After Loaded View From Qml To C++
-
Hello ! I'm new in Qt Devlopment and I have a short question that certainly won't take long time to resolve for such specialists ;)
I made a Loader to display a splashscreen that will load the main.qml thereafter like this:
@
QDeclarativeView view;
view.setSource(QUrl("MainAppLoader.qml"));
view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
view.showFullScreen();@I would like to connect some stuff to a child of my PageStackWindows in main.qml : mainMenu.qml
The problem if I do that :
@ QObject rootObject = dynamic_cast<QObject>(view.rootObject());
QObject mainMenu = rootObject->findChild<QObject>("mainMenu");
QObject::connect(&myClass, SIGNAL(data(QVariant)), mainMenu, SLOT(updateData(QVariant)));@I get this error: QObject::connect: Cannot connect <MyClass>::data(QVariant) to (null)::updateData(QVariant)
Yes mainMenu is null. Of course I put: objectName: "mainMenu"
In my MainAppLoader.qml I have this that load the main.qml :
@Loader {
id: mainLoader
width: parent.width
height: parent.height
}Timer { id: firstPhaseTimer interval: 700 running: true repeat: false onTriggered: { if (!mainLoader.Loading) { // Start to load the main application mainLoader.source = Qt.resolvedUrl("main.qml"); secondPhaseTimer.start(); } } }@
Do you know how I could get / use the reference of my main.qml / MainMenu.qml freshly created to connect what I want from cpp ?
If I do the same without my splashScreenLoader, it works.
Thanks !
-
You are probably asking for connection too early. Either emit a signal to C++ once loading is ready, or make the connection in QML.
-
You say it's possible to connect a freshly created QML View with CPP in QML ?
In other words, I just need to warn Cpp with a signal when my main.qml is created mainLoader.source = Qt.resolvedUrl("main.qml");
to tell him to make the rootObject->findChild<QObject*>("mainMenu"); and connect it ? that's all ?I'l try tomorow, thank you for the quick anwser !
-
I think so. To be honest I did not take time to read through and fully understand your post, I just gave a first impression.
The definite point is that object needs to be ready before meta objects can be connected.
BTW. you don't need the dynamic_cast line, root object should have ::findChild() available.
-
As I see it, you have the following options:
1.) Call a C++ slot after the Loader finished, as sierdzio suggested
You can call a C++ function from QML by registering the C++ object as a context property with @QDeclarativeContext::setContextProperty()@ and then call the slot function of this object. This is useful if the object you are calling only exists once, i.e. a Singleton.2.) By registering the C++ item as a QML item. You can do that with @qmlRegisterType<CppClassName>("NamespaceForImport", 1, 0 "QMLItemName")@
In that way, you could also directly access the signal from QML, e.g. if the signal is called @dataChanged(QVariant newData)@
, you can access it in QML with
@
import NamespaceForImport 1.0QMLItemName {
onDataChanged: { ... "newData" is available as parameter ... }
}
@ -
Hello again, I think I'm doing it wrong
I made a call to my C++ object when QML completed the launched of MainMenu.qml like this:
in main.cpp:
@QDeclarativeView view;
view.rootContext()->setContextProperty("MyClass",myClass);@in MainMenu.qml
@Component.onCompleted: {
MyClass.mainMenuCreated();
}@in MyClass c++
@void MyClass::mainMenuCreated()
{
qDebug() << "Hello I'm created";}@
This is working but now, How can I have access to QDeclarativeView / QDeclarativeContext from MyClass to connect my freshly created qml file with c++ signal ? I only have access in main.cpp and tried to pass it in MyClass constructor, but it seems private. I think I'm doing something wrong :D
-
You should be able to pass it with the constructor - what do you mean that it is private? You have control over MyClass implementation, so you can just do something like the following:
@
//in main.cpp:
QMLApplicationViewer viewer;MyClass myClass = new MyClass(viewer);
//and in MyClass constructor you can access the QDeclarativeEngine from the QMLApplicationViewer with:
MyClass::MyClass(QMLApplicationViewer &viewer) {
QDeclarativeEngine *engine = viewer.engine();
QDeclarativeContext *context = engine->rootContext;
}
@From the QDeclarativeView you can call the method rootContext() to get the context in your MyClass.
Alternatively, to receive the context from a QDeclarativeItem which you instantiate from QML, you can use the following:
@QDeclarativeContext *currentContext = QDeclarativeEngine::contextForObject(this);
QDeclarativeEngine *declarativeEngine = currentContext->engine();
QDeclarativeContext *rootContext = declarativeEngine->rootContext();@ -
I surely made a mistake somewhere :(
In main.cpp
@QDeclarativeView view;
MyClass *myClass = new MyClass(view);
@MyClass.h
@class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass();
explicit MyClass(QDeclarativeView &view);signals:
public slots:
private: QDeclarativeView mQDeclarativeView;
};@
MyClass.cpp
@MyClass::MyClass() {
}MyClass::MyClass(QDeclarativeView &view) {
mQDeclarativeView = view;
}@Got this error:
@ error : 'QDeclarativeView& QDeclarativeView::operator=(const QDeclarativeView&)' is private
@ -
Use a pointer. You are trying to use an assignment operator while all QObject children cannot be copied.
-
It was what I did first but you gave me an example with references :P
It's working now :)
The final code for visitors:
@void MyClass::mainMenuCreated()
{
qDebug() << "Hello I'm created";QObject *menu = (mQDeclarativeView->rootObject())->findChild<QObject*>("mainMenu"); QObject::connect(this, SIGNAL(data(QVariant)), menu, SLOT(updateData(QVariant)));
}@
And now my function from MainMenu.qml is called :)
@updateData(text) { console.log("I'm here"); }
@If you see any improvment that could be made or other better technique, don't hesitate :)
Thank you so much for this lesson :P
-
No worries :)
As a tiny improvement, it's good practice to name context properties with a small first letter to be able to distinguish them from classes/QML components. So instead of "MyClass", name it "myClass" - you could access enums for instance from QML with "MyClass.enumName" then, and so don't mix it up with a context property access.
Also, the data-signal could be renamed to dataChanged, as it is probably reflects more what it does. But that's syntactic sugar, just a suggestion..