Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Binding QQuickWidget to QML contex inside MainWindow
QtWS25 Last Chance

Binding QQuickWidget to QML contex inside MainWindow

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 2 Posters 1.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    vmetodiev
    wrote on last edited by
    #1

    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!

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      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.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1
      • V Offline
        V Offline
        vmetodiev
        wrote on last edited by vmetodiev
        #3

        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?

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          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.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          0
          • V Offline
            V Offline
            vmetodiev
            wrote on last edited by vmetodiev
            #5

            @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.

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              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.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved