Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
qtws26_logo

The Qt Event of the Year: Qt World Summit 2026

Save Your Spot >
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Accessing members of a class defined within another class
Qt 6.12 LTS is out! See what's new in the release blog

Accessing members of a class defined within another class

Scheduled Pinned Locked Moved Solved QML and Qt Quick
12 Posts 4 Posters 1.4k Views 2 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.
  • J.HilkJ J.Hilk

    @_-Peter-_ thats so not possible, because your Value class is not a trivial type.
    I think if you would fully register the class with the meta system you might have more luck accessing the direct member. But, the far easier option would be the following:

    public:
        explicit BackEnd(QObject *parent = nullptr);
    
        QINVOKABLE QVariant getValue1() {return QVariant::fromValue(Value1);} 
        Value Value1;// = Value("km",3);
        Value Value2;// = Value("hr",4)
    
    Text {text: qsTr("Value 1:" + backend.getValue1().Value + " " + backend.getValue1().Units)}
    
    GrecKoG Offline
    GrecKoG Offline
    GrecKo
    Qt Champions 2018
    wrote on last edited by
    #3

    Your bakend object doesn't have any exposed properties named Value1 and Value2.
    Use Q_PROPERTY to expose them like you did in your Value class.

    _ 1 Reply Last reply
    0
    • J.HilkJ J.Hilk

      @_-Peter-_ thats so not possible, because your Value class is not a trivial type.
      I think if you would fully register the class with the meta system you might have more luck accessing the direct member. But, the far easier option would be the following:

      public:
          explicit BackEnd(QObject *parent = nullptr);
      
          QINVOKABLE QVariant getValue1() {return QVariant::fromValue(Value1);} 
          Value Value1;// = Value("km",3);
          Value Value2;// = Value("hr",4)
      
      Text {text: qsTr("Value 1:" + backend.getValue1().Value + " " + backend.getValue1().Units)}
      
      _ Offline
      _ Offline
      _-Peter-_
      wrote on last edited by
      #4

      @J-Hilk Thanks for the response. When I add the line in I get the errors "incomplete result type 'QVariant' in function definition" and "incomplete type 'QVariant' named in nested name specifier"

      class BackEnd : public QObject
      {
          Q_OBJECT
      
      public:
          explicit BackEnd(QObject *parent = nullptr);
      
          Q_INVOKABLE QVariant getValue1(){return QVariant::fromValue(Value1);}
          Value Value1();// = Value("km",3);
          Value Value2;// = Value("hr",4);
      };
      
      J.HilkJ 1 Reply Last reply
      0
      • GrecKoG GrecKo

        Your bakend object doesn't have any exposed properties named Value1 and Value2.
        Use Q_PROPERTY to expose them like you did in your Value class.

        _ Offline
        _ Offline
        _-Peter-_
        wrote on last edited by
        #5

        @GrecKo I had given that a go but got the parse warning " Warning: Property declaration Value1 has no READ accessor function or associated MEMBER variable. The property will be invalid." and compiling ends.

        I tried to a few different variations but got the same error.

        class BackEnd : public QObject
        {
            Q_OBJECT
            Q_PROPERTY(qreal Value1.Value READ Value1.getValue NOTIFY Value1.valueChanged)
            Q_PROPERTY(QString Value1.Units READ Value1.getUnits CONSTANT)
            Q_PROPERTY(qreal Value2.Value READ Value2.getValue NOTIFY Value2.valueChanged)
            Q_PROPERTY(QString Value2.Units READ Value2.getUnits CONSTANT)
        
        public:
            explicit BackEnd(QObject *parent = nullptr);
        
            Value Value1;// = Value("km",3);
            Value Value2;// = Value("hr",4);
        };
        
        1 Reply Last reply
        0
        • _ _-Peter-_

          @J-Hilk Thanks for the response. When I add the line in I get the errors "incomplete result type 'QVariant' in function definition" and "incomplete type 'QVariant' named in nested name specifier"

          class BackEnd : public QObject
          {
              Q_OBJECT
          
          public:
              explicit BackEnd(QObject *parent = nullptr);
          
              Q_INVOKABLE QVariant getValue1(){return QVariant::fromValue(Value1);}
              Value Value1();// = Value("km",3);
              Value Value2;// = Value("hr",4);
          };
          
          J.HilkJ Offline
          J.HilkJ Offline
          J.Hilk
          Moderators
          wrote on last edited by
          #6

          @_-Peter-_ said in Accessing members of a class defined within another class:

          "incomplete type 'QVariant'

          you should know this!

          #include <QVariant>


          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          _ 1 Reply Last reply
          1
          • J.HilkJ J.Hilk

            @_-Peter-_ said in Accessing members of a class defined within another class:

            "incomplete type 'QVariant'

            you should know this!

            #include <QVariant>

            _ Offline
            _ Offline
            _-Peter-_
            wrote on last edited by
            #7

            @J-Hilk Doh! M)

            Now I get a "static assertion failed" during compile and directed to use the Q_DECLARE_METATYPE macro.

            Will read the docs for the Q_DECLARE_METATYPE macro.

            1 Reply Last reply
            1
            • _ _-Peter-_

              Hi Everyone,
              I've been struggling to get the "proof of concept" code at the bottom to work as I want and hoping to get some help.

              The use case is that I'll have a c++ back end processing raw values from an input stream and I want to access these values from the QML UI like this
              Text {text: qsTr("Value 1:" + backend.Value1.Value + " " + backend.Value1.Units)}
              Text {text: qsTr("Value 2:" + backend.Value2.Value + " " + backend.Value2.Units)}

              The code below is just trying to get the QML / C++ interface working before developing the backend.

              I also realise that each Value is being instantiated with the same values but I've been progressively working through the errors thrown during compile and execution and haven't got to sorting this bit.

              The code below compiles but throws "TypeError: Cannot read property 'Value' of undefined" for the two text lines in main.qml.

              Following this example https://doc.qt.io/qt-5/qtqml-cppintegration-topic.html I can get the functionality I want for a single value when it's not a member of another class. While I could create a separate class for each value I wouldn't be following good object orientated practice particularly as I have about 60 different values to access from QML.

              As much as I've read the documents on Integrating QML and C++ and tried various versions of the code below, I've got no idea how to get QML to access the members of the Value class.

              Appreciate any help and direction.
              Peter

              backend.h

              #ifndef BACKEND_H
              #define BACKEND_H
              
              #include <QObject>
              #include <QString>
              
              class Value : public QObject
              {
                  Q_OBJECT
                  Q_PROPERTY(qreal Value READ getValue NOTIFY valueChanged)
                  Q_PROPERTY(QString Units READ getUnits CONSTANT)
              
              public:
                  explicit Value(QObject *parent = nullptr);
                  // Value(QString _units, qreal _scale);
                  Value(QString _units, qreal _scale);
              
                  qreal getValue();
                  QString getUnits();
                  void newRawValue(qreal _rawValue);
              
              signals:
                  void valueChanged();
              
              private:
                  QString units = ""; //prefer to be const
                  qreal rawValue = 0;
                  qreal scale = 0; //prefer to be const
              };
              
              class BackEnd : public QObject
              {
                  Q_OBJECT
              
              public:
                  explicit BackEnd(QObject *parent = nullptr);
              
                  Value Value1;// = Value("km",3);
                  Value Value2;// = Value("hr",4);
              };
              
              #endif // BACKEND_H
              

              backend.ccp

              #include "backend.h"
              
              Value::Value(QObject *parent) : QObject(parent){
              }
              //Value::Value(QString _units, qreal _scale){
              //    units = _units;
              //    scale = _scale;
              //}
              qreal Value :: getValue(){return rawValue*scale;}
              
              QString Value :: getUnits(){return units;}
              
              void Value :: newRawValue(qreal _rawValue){
                  rawValue = _rawValue;
                  emit valueChanged();
              }
              
              BackEnd::BackEnd(QObject *parent) : QObject(parent){
              }
              

              main.cpp

              #include <QGuiApplication>
              #include <QQmlApplicationEngine>
              
              #include "backend.h"
              
              int main(int argc, char *argv[])
              {
                  QGuiApplication app(argc, argv);
              
                  qmlRegisterType<BackEnd>("io.qt.examples.backend", 1, 0, "BackEnd");
              
                  QQmlApplicationEngine engine;
                  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
              
                  return app.exec();
              }
              

              main.qml

              import QtQuick 2.12
              import QtQuick.Window 2.12
              
              import io.qt.examples.backend 1.0
              
              Window {
                  visible: true
                  width: 640
                  height: 480
              
                  BackEnd {id: backend}
              
                  Text {text: qsTr("Value 1:" + backend.Value1.Value + " " + backend.Value1.Units)}
                  Text {text: qsTr("Value 2:" + backend.Value2.Value + " " + backend.Value2.Units)}
              }
              
              
              jeremy_kJ Offline
              jeremy_kJ Offline
              jeremy_k
              wrote on last edited by jeremy_k
              #8

              This is strictly practical advice. If this is an intellectual exercise, please ignore. All sorts of interesting things happen when the rules aren't followed.

              @_-Peter-_ said in Accessing members of a class defined within another class:

              Q_PROPERTY(qreal Value1.Value READ Value1.getValue NOTIFY Value1.valueChanged)
              

              In QML, not solely of custom, . is a token separator, and a token starting with an upper-case letter is a type.

              https://doc.qt.io/qt-6/qtqml-documents-definetypes.html#naming-custom-qml-object-types:

              It must begin with an uppercase letter.

              Ignoring this leads to headaches. If the goal is working code rather than exploring the limits of the framework, do not use . or strings starting with upper case letters in property names.

              @_-Peter-_ said in Accessing members of a class defined within another class:

              backend.h

              [...]
              

              backend.ccp

              [...]
              

              Only tangentially connected, but defining separate .h, .cpp, and main.cpp files for an example makes the code harder to follow for a forum post. My not at all humble opinion is that everything should be inlined to focus on compilation units rather than files.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              _ 1 Reply Last reply
              0
              • jeremy_kJ jeremy_k

                This is strictly practical advice. If this is an intellectual exercise, please ignore. All sorts of interesting things happen when the rules aren't followed.

                @_-Peter-_ said in Accessing members of a class defined within another class:

                Q_PROPERTY(qreal Value1.Value READ Value1.getValue NOTIFY Value1.valueChanged)
                

                In QML, not solely of custom, . is a token separator, and a token starting with an upper-case letter is a type.

                https://doc.qt.io/qt-6/qtqml-documents-definetypes.html#naming-custom-qml-object-types:

                It must begin with an uppercase letter.

                Ignoring this leads to headaches. If the goal is working code rather than exploring the limits of the framework, do not use . or strings starting with upper case letters in property names.

                @_-Peter-_ said in Accessing members of a class defined within another class:

                backend.h

                [...]
                

                backend.ccp

                [...]
                

                Only tangentially connected, but defining separate .h, .cpp, and main.cpp files for an example makes the code harder to follow for a forum post. My not at all humble opinion is that everything should be inlined to focus on compilation units rather than files.

                _ Offline
                _ Offline
                _-Peter-_
                wrote on last edited by
                #9

                @jeremy_k Thanks for the advice.

                1 Reply Last reply
                0
                • _ Offline
                  _ Offline
                  _-Peter-_
                  wrote on last edited by
                  #10

                  After almost turning software problems into a hardware problem a few times, I finally got it to work as I intended.

                  With the addition of Q_INVOKABLE as suggested by @J-Hilk and the removal of the Value(QObject *parent) constructor, it all works.

                  Final code for prosperity below:
                  backend.h

                  #ifndef BACKEND_H
                  #define BACKEND_H
                  
                  #include <QObject>
                  #include <QString>
                  #include <QVariant>
                  
                  
                  class Value : public QObject
                  {
                      Q_OBJECT
                      Q_PROPERTY(qreal value READ getValue NOTIFY valueChanged)
                      Q_PROPERTY(QString units READ getUnits CONSTANT)
                  
                  public:
                      Value(QString _units, qreal _scale);
                      ~Value();
                  
                      qreal getValue() const;
                      QString getUnits() const;
                      void newRawValue(qreal _rawValue);
                  
                  signals:
                      void valueChanged();
                  
                  private:
                      const QString units;
                      qreal rawValue = 1;
                      const qreal scale;
                  };
                  
                  class BackEnd : public QObject
                  {
                      Q_OBJECT
                  
                  public:
                      explicit BackEnd(QObject *parent = nullptr);
                  
                      Q_INVOKABLE QVariant getValue1(){return QVariant::fromValue(value1);}
                      Q_INVOKABLE QVariant getValue2(){return QVariant::fromValue(value2);}
                  
                  private:
                      Value *value1 = new Value("km",3);
                      Value *value2 = new Value("hr",4);
                  };
                  
                  #endif // BACKEND_H
                  

                  backend.cpp

                  #include "backend.h"
                  
                  Value::Value(QString _units, qreal _scale): units(_units), scale(_scale){}
                  
                  Value::~Value(){}
                  
                  qreal Value :: getValue() const {return rawValue*scale;}
                  
                  QString Value :: getUnits()const {return units;}
                  
                  void Value :: newRawValue(qreal _rawValue){
                      if(rawValue == _rawValue)
                          return;
                  
                      rawValue = _rawValue;
                      emit valueChanged();
                  }
                  
                  BackEnd :: BackEnd(QObject *parent) : QObject(parent){
                  }
                  

                  main.cpp

                  #include <QGuiApplication>
                  #include <QQmlApplicationEngine>
                  
                  #include "backend.h"
                  
                  int main(int argc, char *argv[])
                  {
                      QGuiApplication app(argc, argv);
                  
                      qmlRegisterType<BackEnd>("backend", 1, 0, "BackEnd");
                  
                      QQmlApplicationEngine engine;
                      engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                  
                      return app.exec();
                  }
                  

                  main.qml

                  import QtQuick 2.12
                  import QtQuick.Window 2.12
                  
                  import backend 1.0
                  
                  Window {
                      visible: true
                      width: 640
                      height: 480
                  
                      BackEnd {id: backend}
                  
                      Text {text: qsTr("Value 1: " + backend.getValue1().value + " " + backend.getValue1().units)}
                      Text {y: 20;text: qsTr("Value 2: " + backend.getValue2().value + " " + backend.getValue2().units)}
                  }
                  
                  GrecKoG 1 Reply Last reply
                  1
                  • _ _-Peter-_ has marked this topic as solved on
                  • _ _-Peter-_

                    After almost turning software problems into a hardware problem a few times, I finally got it to work as I intended.

                    With the addition of Q_INVOKABLE as suggested by @J-Hilk and the removal of the Value(QObject *parent) constructor, it all works.

                    Final code for prosperity below:
                    backend.h

                    #ifndef BACKEND_H
                    #define BACKEND_H
                    
                    #include <QObject>
                    #include <QString>
                    #include <QVariant>
                    
                    
                    class Value : public QObject
                    {
                        Q_OBJECT
                        Q_PROPERTY(qreal value READ getValue NOTIFY valueChanged)
                        Q_PROPERTY(QString units READ getUnits CONSTANT)
                    
                    public:
                        Value(QString _units, qreal _scale);
                        ~Value();
                    
                        qreal getValue() const;
                        QString getUnits() const;
                        void newRawValue(qreal _rawValue);
                    
                    signals:
                        void valueChanged();
                    
                    private:
                        const QString units;
                        qreal rawValue = 1;
                        const qreal scale;
                    };
                    
                    class BackEnd : public QObject
                    {
                        Q_OBJECT
                    
                    public:
                        explicit BackEnd(QObject *parent = nullptr);
                    
                        Q_INVOKABLE QVariant getValue1(){return QVariant::fromValue(value1);}
                        Q_INVOKABLE QVariant getValue2(){return QVariant::fromValue(value2);}
                    
                    private:
                        Value *value1 = new Value("km",3);
                        Value *value2 = new Value("hr",4);
                    };
                    
                    #endif // BACKEND_H
                    

                    backend.cpp

                    #include "backend.h"
                    
                    Value::Value(QString _units, qreal _scale): units(_units), scale(_scale){}
                    
                    Value::~Value(){}
                    
                    qreal Value :: getValue() const {return rawValue*scale;}
                    
                    QString Value :: getUnits()const {return units;}
                    
                    void Value :: newRawValue(qreal _rawValue){
                        if(rawValue == _rawValue)
                            return;
                    
                        rawValue = _rawValue;
                        emit valueChanged();
                    }
                    
                    BackEnd :: BackEnd(QObject *parent) : QObject(parent){
                    }
                    

                    main.cpp

                    #include <QGuiApplication>
                    #include <QQmlApplicationEngine>
                    
                    #include "backend.h"
                    
                    int main(int argc, char *argv[])
                    {
                        QGuiApplication app(argc, argv);
                    
                        qmlRegisterType<BackEnd>("backend", 1, 0, "BackEnd");
                    
                        QQmlApplicationEngine engine;
                        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                    
                        return app.exec();
                    }
                    

                    main.qml

                    import QtQuick 2.12
                    import QtQuick.Window 2.12
                    
                    import backend 1.0
                    
                    Window {
                        visible: true
                        width: 640
                        height: 480
                    
                        BackEnd {id: backend}
                    
                        Text {text: qsTr("Value 1: " + backend.getValue1().value + " " + backend.getValue1().units)}
                        Text {y: 20;text: qsTr("Value 2: " + backend.getValue2().value + " " + backend.getValue2().units)}
                    }
                    
                    GrecKoG Offline
                    GrecKoG Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on last edited by
                    #11

                    Q_INVOKABLE leads to worse QML code here.

                    Semantically, Value1 and Value2 are properties of your Backend.
                    Invokable function are for functions, stuff that mutates your state or compute something, that's not the case here.

                    If the Value1 and Value2 pointers never change, expose them as Q_PROPERTY in Backend.h:

                    Q_PROPERTY(Value* value1 READ getValue1 CONSTANT)
                    If for some reason you don't want to register the Value type, you don't have to use QVariant, you can expose it as a Q_PROPERTY(QObject*, ...

                    Keep your getters as is and just delete the Q_INVOKABLE part.

                    QML code would then be:

                    Text {text: qsTr("Value 1: " + backend.value1.value + " " + backend.value1.units)}
                    Text {y: 20;text: qsTr("Value 2: " + backend.value2.value + " " + backend.value2.units)}
                    

                    Which can be rewritten with a Column and a repeater:

                    Column {
                        Repeater {
                            model: [backend.value1, backend.value2]
                            delegate: Text {
                                text: qsTr("Value %1: %2 %3 ").arg(index).arg(modelData.value).arg(modelData.units)
                                padding: 8
                        }
                    }
                    

                    And then depending on the actual usecase you might consider exposing your values in a model instead of named properties for each Value1, Value2

                    _ 1 Reply Last reply
                    0
                    • GrecKoG GrecKo

                      Q_INVOKABLE leads to worse QML code here.

                      Semantically, Value1 and Value2 are properties of your Backend.
                      Invokable function are for functions, stuff that mutates your state or compute something, that's not the case here.

                      If the Value1 and Value2 pointers never change, expose them as Q_PROPERTY in Backend.h:

                      Q_PROPERTY(Value* value1 READ getValue1 CONSTANT)
                      If for some reason you don't want to register the Value type, you don't have to use QVariant, you can expose it as a Q_PROPERTY(QObject*, ...

                      Keep your getters as is and just delete the Q_INVOKABLE part.

                      QML code would then be:

                      Text {text: qsTr("Value 1: " + backend.value1.value + " " + backend.value1.units)}
                      Text {y: 20;text: qsTr("Value 2: " + backend.value2.value + " " + backend.value2.units)}
                      

                      Which can be rewritten with a Column and a repeater:

                      Column {
                          Repeater {
                              model: [backend.value1, backend.value2]
                              delegate: Text {
                                  text: qsTr("Value %1: %2 %3 ").arg(index).arg(modelData.value).arg(modelData.units)
                                  padding: 8
                          }
                      }
                      

                      And then depending on the actual usecase you might consider exposing your values in a model instead of named properties for each Value1, Value2

                      _ Offline
                      _ Offline
                      _-Peter-_
                      wrote on last edited by
                      #12

                      @GrecKo Thanks for that it does make sense.

                      You are correct that value1 & 2 pointers never change. In the actual use case there will be other unexposed backend methods that update Value's rawValue which is then scaled to real units for the QML layer to read. All of Value's exposed methods and properties will be only be read by the QML layer. There will be exposed backend methods that are written to by the QML layer and other values that will be exposed in a model, but this is straight forward as there's plenty of information out there on doing that.

                      Can you please confirm your recommended changes to the code I previously posted. My understanding of the changes is below but that throws a "cannot convert 'QVariant' to 'Value*' in assignment." during the build.

                      class BackEnd : public QObject
                      {
                          Q_OBJECT
                          Q_PROPERTY(Value* value1 READ getValue1 CONSTANT)
                          Q_PROPERTY(Value* value2 READ getValue2 CONSTANT)
                      public:
                          explicit BackEnd(QObject *parent = nullptr);
                      
                         QVariant getValue1(){return QVariant::fromValue(value1);}
                         QVariant getValue2(){return QVariant::fromValue(value2);}
                      

                      When I change the Q_PROPERTY lines to below everything works

                       Q_PROPERTY(QVariant value1 READ getValue1 CONSTANT)
                      

                      Thanks again for your assistance!

                      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
                      • Unsolved