Binding QQuickWidget to QML contex inside MainWindow
-
Dear Community,
I have an embedded QQuickWidget inside my QApplication. In order to be able to manipulate the QML part, I am populating the context inside MainWindow::MainWindow via the following lines:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); this->tableRows = 0; // The map QQmlApplicationEngine engine; QQmlContext *context = engine.rootContext(); Map mapConnectionObj; context->setContextProperty("mapConnection", &mapConnectionObj); engine.load(QUrl::fromLocalFile("../TrackServ/map.qml")); QObject *rootObject = engine.rootObjects().first(); Map* map = new Map(rootObject, "osmMap"); if (map == nullptr) { qDebug() << "ERROR: QML element not found..."; } else { qDebug() << "QML element found..."; } qDebug() << "Loading the map widget..."; ui->quickWidget->setSource(QUrl::fromLocalFile("../TrackServ/map.qml")); //.....
As far as I know, the "context->setContextProperty" should always be invoked before loading the QML file to the engine. But in order to populate the MainWindow with the QQuickWidget, another duplicate logic is being used that does not bind properly to the QML (and it seems like a separate instanced is being created as a result):
ui->quickWidget->setSource(QUrl::fromLocalFile("../TrackServ/map.qml"));
As a result, the QML connection mapping fails:
Loading the map widget... map.qml:31:9: QML Connections: Cannot assign to non-existent property "onSendToQml" map.qml:32: ReferenceError: mapConnection is not defined
May someone advise what is the correct flow to establish the QML Connections with the C++ "back-end" in this scenario?
Thank you in advance!
-
Hi,
Your engine object is local to the constructor and thus will be destroyed by the end of it.
That said, you want to use the engine created along the QQuickWidget object you have. Or directly the root object or context depending on what you want to do. The class has APIs to get each of them.
-
Hi,
It worked. Thank you once again!!! The connection between the QML and the MainWindow is established and I am able to emit a signal to the QML and I get the result of my JavaScript function inside the QML.
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); this->tableRows = 0; // The map // // // qDebug() << "Loading the map widget..."; ui->quickWidget->setSource(QUrl::fromLocalFile("../TrackServ/map.qml")); QQuickWidget::Status qmlStatus = QQuickWidget::Loading; while(qmlStatus != QQuickWidget::Ready) { qmlStatus = ui->quickWidget->status(); }; QQmlContext *context = ui->quickWidget->engine()->rootContext(); QQuickItem *rootObject = ui->quickWidget->rootObject(); Map* map = new Map(rootObject, "osmMap"); context->setContextProperty("mapConnection", map); qDebug() << "End of loading the map widget..."; // // //
There is a small detail in the log - I still get the following messages upon launching the application:
map.qml:31:9: QML Connections: Cannot assign to non-existent property "onSendToQml" map.qml:32: ReferenceError: mapConnection is not defined
I think it does not affect the expected functionality at all, but is there anything else that can be done to make it perfect and get rid of the error?
-
Not knowing your QML code nor the Map class I would say that you are trying to use a signal as if was part of a property but it is not.
-
@SGaist said in Binding QQuickWidget to QML contex inside MainWindow:
would say that you are trying to use a signal as if was part of a property but it is not.
map.qml:
import QtQuick 2.0 import QtQuick.Window 2.0 import QtLocation 5.6 import QtPositioning 5.6 Item { objectName: "mapItem" Plugin { id: osmPlugin objectName: "osmPlugin" name: "osm" PluginParameter { name: "osm.mapping.providersrepository.disabled" value: "true" } PluginParameter { name: "osm.mapping.providersrepository.address" value: "http://maps-redirect.qt.io/osm/5.6/" } } Map { id: osmMap objectName: "osmMap" anchors.fill: parent plugin: osmPlugin center: QtPositioning.coordinate(59.91, 10.75) // Oslo zoomLevel: 11 Connections { target: mapConnection onChangeLocation: function handleEvent() { console.log("Event reaction..."); osmMap.center = QtPositioning.coordinate(43.0757, 25.6172); } } } }
map.h:
#ifndef MAP_H #define MAP_H #include <QObject> #include <QQuickItem> class Map : public QObject { Q_OBJECT public: explicit Map(QObject *parent = nullptr); Map(QObject* qmlRootObject, QString specificObject); Map(QQuickItem* qmlRootObject, QString specificObject); ~Map(); private: QObject* openStreetMap = nullptr; QObject* openStreetMapCenterObject = nullptr; signals: void changeLocation(); public slots: void receiveFromMainWindow(); void logLocation(); }; #endif // MAP_H
map.cpp:
#include "map.h" #include <QDebug> #include <QGeoCoordinate> #include <QtPositioning> #include <QtLocation> Map::Map(QObject* parent) : QObject(parent) { // This is the implementation of the explicit constructor } Map::Map(QQuickItem* qmlRootObject, QString specificObject) : QObject (Q_NULLPTR) { QObject* qmlSpecificObject = qmlRootObject->findChild<QObject*>(specificObject); this->openStreetMap = qmlSpecificObject; this->openStreetMapCenterObject = qvariant_cast<QObject*>(openStreetMap->property("center")); // Do something auto coordinates = this->openStreetMap->property("center").value<QGeoCoordinate>(); qDebug() << "Coordinates (original): " << coordinates; coordinates.setLatitude(coordinates.latitude() + 2); coordinates.setLatitude(coordinates.longitude() + 3); qDebug() << "Coordinates (new): " << coordinates; } void Map::receiveFromMainWindow() { qDebug() << "SLOT: Inside receiveFromMainWindow()"; emit changeLocation(); } void Map::logLocation() { qDebug() << "SLOT: Inside logLocation()"; } Map::~Map() { }
The only signal I have is "changeLocation". The QML definition is according to the "on..." convention.
-
You should avoid creating a class that has the exact same name of an existing QML type. It makes things confusing and hard to reason about.