Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML: "Cannot assign Derived to Base"
Forum Updated to NodeBB v4.3 + New Features

QML: "Cannot assign Derived to Base"

Scheduled Pinned Locked Moved Solved QML and Qt Quick
9 Posts 3 Posters 736 Views 3 Watching
  • 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.
  • A Offline
    A Offline
    alexcrist
    wrote on last edited by
    #1

    Hello,

    I've been trying for 2 days to find an answer to this, but either I've been using the wrong (key)words, either there's no answer.

    I have in C++ a class 'Point' derived from QGeoCoordinate and marked with Q_GADGET (just like QGeoCoordinate). Metatype is also declared as "Q_DECLARE_METATYPE(Point)"

    When an instance of this class reaches QML (either by Q_PROPERTY of another class, or an argument of a signal), everything can be read correctly from the object (latitude, longitude from QGeoCoordinate, and other members from Point). But if I try to assign it to something that needs a QGeoCoordinate (for instance, a map.center, or a polyline point), I get a QML error on runtime: "Error: Cannot assign Point to QGeoCoordinate"

    Since Point derives QGeoCoordinate, I expected this to work. Am I doing something wrong, or this is not possible?

    Here's my sample code:
    point.h:

    #pragma once
    #include <QGeoCoordinate>
    
    class Point : public QGeoCoordinate
    {
        Q_GADGET
        Q_PROPERTY(float azimuth READ getAzimuth CONSTANT)
    
    public:
        Point()
        {
            setLatitude(25);
            setLongitude(25);
        }
    
        float getAzimuth() const { return m_azimuth; }
    
    private:
        float m_azimuth = 13.15;
    };
    
    Q_DECLARE_METATYPE(Point)
    

    controller.h

    #pragma once
    #include "point.h"
    #include <QObject>
    
    class Controller : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(Point point READ getPoint CONSTANT)
    
    public:
        Controller() { }
        Point getPoint() const { return point; }
    
    private:
        Point point;
    };
    

    main.cpp:

    #include "controller.h"
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    
    int main(int argc, char *argv[])
    {
        QGuiApplication a(argc, argv);
        QQmlApplicationEngine engine;
        Controller controller;
    
        engine.rootContext()->setContextProperty("controller", &controller);
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        return a.exec();
    }
    

    main.qml:

    import QtQuick 2.15
    import QtQuick.Controls 1.4
    import QtLocation 5.15
    import QtQuick.Window 2.2
    
    Window {
        visible: true
        visibility: "Maximized"
    
        Plugin {
            id: mapPlugin
            name: "esri"
        }
    
        Map {
            id: map
            anchors.fill: parent
            plugin: mapPlugin
        }
    
        Button {
            text: "clic"
            onClicked: {
                console.log(controller.point.azimuth)
                console.log(controller.point.latitude)
                console.log(controller.point.longitude)
    
    
                map.center = controller.point // => qrc:/main.qml:28: Error: Cannot assign Point to QGeoCoordinate
            }
        }
    }
    

    Thanks!

    raven-worxR 1 Reply Last reply
    0
    • A alexcrist

      Hello,

      I've been trying for 2 days to find an answer to this, but either I've been using the wrong (key)words, either there's no answer.

      I have in C++ a class 'Point' derived from QGeoCoordinate and marked with Q_GADGET (just like QGeoCoordinate). Metatype is also declared as "Q_DECLARE_METATYPE(Point)"

      When an instance of this class reaches QML (either by Q_PROPERTY of another class, or an argument of a signal), everything can be read correctly from the object (latitude, longitude from QGeoCoordinate, and other members from Point). But if I try to assign it to something that needs a QGeoCoordinate (for instance, a map.center, or a polyline point), I get a QML error on runtime: "Error: Cannot assign Point to QGeoCoordinate"

      Since Point derives QGeoCoordinate, I expected this to work. Am I doing something wrong, or this is not possible?

      Here's my sample code:
      point.h:

      #pragma once
      #include <QGeoCoordinate>
      
      class Point : public QGeoCoordinate
      {
          Q_GADGET
          Q_PROPERTY(float azimuth READ getAzimuth CONSTANT)
      
      public:
          Point()
          {
              setLatitude(25);
              setLongitude(25);
          }
      
          float getAzimuth() const { return m_azimuth; }
      
      private:
          float m_azimuth = 13.15;
      };
      
      Q_DECLARE_METATYPE(Point)
      

      controller.h

      #pragma once
      #include "point.h"
      #include <QObject>
      
      class Controller : public QObject
      {
          Q_OBJECT
          Q_PROPERTY(Point point READ getPoint CONSTANT)
      
      public:
          Controller() { }
          Point getPoint() const { return point; }
      
      private:
          Point point;
      };
      

      main.cpp:

      #include "controller.h"
      #include <QGuiApplication>
      #include <QQmlApplicationEngine>
      #include <QQmlContext>
      
      int main(int argc, char *argv[])
      {
          QGuiApplication a(argc, argv);
          QQmlApplicationEngine engine;
          Controller controller;
      
          engine.rootContext()->setContextProperty("controller", &controller);
          engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
      
          return a.exec();
      }
      

      main.qml:

      import QtQuick 2.15
      import QtQuick.Controls 1.4
      import QtLocation 5.15
      import QtQuick.Window 2.2
      
      Window {
          visible: true
          visibility: "Maximized"
      
          Plugin {
              id: mapPlugin
              name: "esri"
          }
      
          Map {
              id: map
              anchors.fill: parent
              plugin: mapPlugin
          }
      
          Button {
              text: "clic"
              onClicked: {
                  console.log(controller.point.azimuth)
                  console.log(controller.point.latitude)
                  console.log(controller.point.longitude)
      
      
                  map.center = controller.point // => qrc:/main.qml:28: Error: Cannot assign Point to QGeoCoordinate
              }
          }
      }
      

      Thanks!

      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by raven-worx
      #2

      @alexcrist
      i dont see that you are registering your derived type to QML?!
      registering a extending type should be enough i think:
      https://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html#registering-extension-objects

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      1 Reply Last reply
      0
      • A Offline
        A Offline
        alexcrist
        wrote on last edited by
        #3

        @raven-worx Indeed I didn't. But the documentation states that ”An extension class is a regular QObject, with a constructor that takes a QObject pointer.” and ”This takes effect if the type is exposed to QML using a QML_ELEMENT or QML_NAMED_ELEMENT() macro.”, neither of which applies here.

        Both QGeoCoordinate and Point are Q_GADGETs, not Q_OBJECTs. Am I mistaking that QML_EXTENDED doesn't work with Q_GADGETs?

        And, as I said, the Point instance can be accessed correctly in QML, the 3 console.logs work just fine, so the type itself seems to be registered in QML. The issue is with assigning it to other QGeoCoordinate properties of other objects.

        raven-worxR 1 Reply Last reply
        0
        • A alexcrist

          @raven-worx Indeed I didn't. But the documentation states that ”An extension class is a regular QObject, with a constructor that takes a QObject pointer.” and ”This takes effect if the type is exposed to QML using a QML_ELEMENT or QML_NAMED_ELEMENT() macro.”, neither of which applies here.

          Both QGeoCoordinate and Point are Q_GADGETs, not Q_OBJECTs. Am I mistaking that QML_EXTENDED doesn't work with Q_GADGETs?

          And, as I said, the Point instance can be accessed correctly in QML, the 3 console.logs work just fine, so the type itself seems to be registered in QML. The issue is with assigning it to other QGeoCoordinate properties of other objects.

          raven-worxR Offline
          raven-worxR Offline
          raven-worx
          Moderators
          wrote on last edited by
          #4

          @alexcrist said in QML: "Cannot assign Derived to Base":

          And, as I said, the Point instance can be accessed correctly in QML, the 3 console.logs work just fine

          i assume it works because the properties are taken from the known QGeoCoordinate qml type.

          Does it work when you simply change the type of the qproperty definition to QGeoCoordinate?

          Q_PROPERTY(QGeoCoordinate point READ getPoint CONSTANT)
          

          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
          If you have a question please use the forum so others can benefit from the solution in the future

          1 Reply Last reply
          0
          • A Offline
            A Offline
            alexcrist
            wrote on last edited by alexcrist
            #5

            There are 3 logs there:

            console.log(controller.point.azimuth) // =>prints correctly, from Point
            console.log(controller.point.latitude) // =>prints correctly, from QGeoCoordinate (base)
            console.log(controller.point.longitude) // =>prints correctly, from QGeoCoordinate (base)
            
            map.center = controller.point // => fails with: qrc:/main.qml:28: Error: Cannot assign Point to QGeoCoordinate
            

            The first one, azimuth, belongs to the Point class, the other 2 are from QGeoCoordinate. Map.center fails

            If I change the Q_PROPERTY as you suggested (and NO other changes anywhere), the code builds properly, and the behavior changes:

            console.log(controller.point.azimuth) // => fails: prints ”undefined” (makes sense, can't access Point properties)
            console.log(controller.point.latitude) // =>prints correctly, from QGeoCoordinate
            console.log(controller.point.longitude) // =>prints correctly, from QGeoCoordinate
            
            map.center = controller.point // => works correctly!
            

            So, not quite there yet... :(

            raven-worxR 1 Reply Last reply
            0
            • A alexcrist

              There are 3 logs there:

              console.log(controller.point.azimuth) // =>prints correctly, from Point
              console.log(controller.point.latitude) // =>prints correctly, from QGeoCoordinate (base)
              console.log(controller.point.longitude) // =>prints correctly, from QGeoCoordinate (base)
              
              map.center = controller.point // => fails with: qrc:/main.qml:28: Error: Cannot assign Point to QGeoCoordinate
              

              The first one, azimuth, belongs to the Point class, the other 2 are from QGeoCoordinate. Map.center fails

              If I change the Q_PROPERTY as you suggested (and NO other changes anywhere), the code builds properly, and the behavior changes:

              console.log(controller.point.azimuth) // => fails: prints ”undefined” (makes sense, can't access Point properties)
              console.log(controller.point.latitude) // =>prints correctly, from QGeoCoordinate
              console.log(controller.point.longitude) // =>prints correctly, from QGeoCoordinate
              
              map.center = controller.point // => works correctly!
              

              So, not quite there yet... :(

              raven-worxR Offline
              raven-worxR Offline
              raven-worx
              Moderators
              wrote on last edited by
              #6

              @alexcrist
              i looked into the QML internals and you cannot assign a value with a custom type to a property (QObject pointers are a special case - and some others)

              For your case i suggest you either subclass Point from QObject wich returns a QGeoCoordinate in a property or invokable method (e.g. toGeoCoordinate()), or the controller returns a copy of your Point as a QGeoCoordinate.

              Either way you cannot subclass all value types and assing them to a property expecting a specific type, since the registered meta-type id is used for comparison. Thus the Point meta-type cannot be "casted" to the QGeoCoordinate meta type.

              --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
              If you have a question please use the forum so others can benefit from the solution in the future

              GrecKoG 1 Reply Last reply
              0
              • raven-worxR raven-worx

                @alexcrist
                i looked into the QML internals and you cannot assign a value with a custom type to a property (QObject pointers are a special case - and some others)

                For your case i suggest you either subclass Point from QObject wich returns a QGeoCoordinate in a property or invokable method (e.g. toGeoCoordinate()), or the controller returns a copy of your Point as a QGeoCoordinate.

                Either way you cannot subclass all value types and assing them to a property expecting a specific type, since the registered meta-type id is used for comparison. Thus the Point meta-type cannot be "casted" to the QGeoCoordinate meta type.

                GrecKoG Offline
                GrecKoG Offline
                GrecKo
                Qt Champions 2018
                wrote on last edited by
                #7

                @raven-worx said in QML: "Cannot assign Derived to Base":

                i looked into the QML internals and you cannot assign a value with a custom type to a property (QObject pointers are a special case - and some others)

                What does that refer to? Assigning a to a custom Q_GADGET property?
                Because you can do that.

                raven-worxR 1 Reply Last reply
                0
                • GrecKoG GrecKo

                  @raven-worx said in QML: "Cannot assign Derived to Base":

                  i looked into the QML internals and you cannot assign a value with a custom type to a property (QObject pointers are a special case - and some others)

                  What does that refer to? Assigning a to a custom Q_GADGET property?
                  Because you can do that.

                  raven-worxR Offline
                  raven-worxR Offline
                  raven-worx
                  Moderators
                  wrote on last edited by raven-worx
                  #8

                  @GrecKo
                  depending on the type of declared property.
                  the meta type system doesnt check if a metatype derives from another metatype.

                  in this case the type of the property is QGeoCoordinate, and only accepts values of this type and no others.

                  https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/qml/jsruntime/qv4qobjectwrapper.cpp?h=v5.14.0#n505

                  --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                  If you have a question please use the forum so others can benefit from the solution in the future

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    alexcrist
                    wrote on last edited by alexcrist
                    #9

                    Well, that's too bad. :(

                    Thanks for your help. I've applied a workaround like this in Point:

                    Q_PROPERTY(QGeoCoordinate coordinates READ coordinates CONSTANT)
                    const QGeoCoordinate &coordinates() const { return *this; }
                    

                    And seems to do the job. It's not ideal, since I also have a QList<Point> (used in a Repeater in QML), and there I need to make a copy of the list with casted objects. But if that's the only way it can work now... so be it. :)

                    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