QML: Type Error with custom QObject

10,035

It helps if you break down the expression on the line where the error is coming from. Try just printing myData.a first:

print(myData.a)
myData.a = mouse.x;
myData.b = mouse.y;

qrc:///main.qml:31: TypeError: Cannot read property 'a' of null

So, myData is null. We can verify this with another QObject-based type:

MouseArea {
    property DataObject myData;
    property Item item
    anchors.fill: parent
    drag.target: parent

    onReleased: {
        print(item)
        myData.a = mouse.x;
        myData.b = mouse.y;
    }
}

qml: null

So, you can fix this error by initialising the property:

property DataObject myData: DataObject {}

You can think of QObject-based properties as pointers for JavaScript; they can be null or point to a valid object... or be undefined. :) I can't find anything mentioned about this here, but that's where this behaviour should be mentioned.

If you'd like to simplify things, you can have the object default-constructed for you by making it a child object of the MouseArea, rather than a property:

MouseArea {
    DataObject {
        id: myData
    }
    anchors.fill: parent
    drag.target: parent

    onReleased: {
        myData.a = mouse.x;
        myData.b = mouse.y;
    }
}

Currently, you won't be able to refer to this property from C++. However, you can achieve this in two ways:

  1. Declare a property alias to the item.
  2. Give it an objectName and use QObject::findChild to find it.
Share:
10,035
Doug
Author by

Doug

Updated on July 02, 2022

Comments

  • Doug
    Doug almost 2 years

    I'm trying out something in QML to try and make it alittle easier to merge the two more seamlessly; to be precise, I'm trying to link an object with structured data to QML.

    I have the following setup:

    main.cpp:

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

    dataobject.h:

    #ifndef DATAOBJECT_H
    #define DATAOBJECT_H
    
    #include <QObject>
    #include <QVariant>
    #include <iostream>
    
    class DataObject : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(qreal a MEMBER a NOTIFY aChanged)
        Q_PROPERTY(qreal b MEMBER b NOTIFY bChanged)
    public:
        explicit DataObject(QObject *parent = 0);
    
    signals:
        void aChanged();
        void bChanged();
    
    public slots:
        void printState() {
            using namespace std;
            cout << a << ", " << b << endl;
        }
    
    private:
        qreal a;
        qreal b;
    };
    
    #endif // DATAOBJECT_H
    

    dataobject.cpp:

    #include "dataobject.h"
    
    DataObject::DataObject(QObject *parent) :
        QObject(parent)
    {
        connect(this, SIGNAL(aChanged()), this, SLOT(printState()));
        connect(this, SIGNAL(bChanged()), this, SLOT(printState()));
    }
    

    main.qml:

    import DO 1.0
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        menuBar: MenuBar {
            Menu {
                title: qsTr("File")
                MenuItem {
                    text: qsTr("Exit")
                    onTriggered: Qt.quit();
                }
            }
        }
        Text {
            text: qsTr("Hello World")
    
            MouseArea {
                property DataObject myData;
                anchors.fill: parent
                drag.target: parent
    
                onReleased: {
                    myData.a = mouse.x;
                    myData.b = mouse.y;
                }
            }
        }
    }
    

    qml.qrc:

    <RCC>
        <qresource prefix="/">
            <file>main.qml</file>
        </qresource>
    </RCC>
    

    Now, what I'd hoped was that values generated by QML could be feed into a object in C++ directly (i.e. the onReleased handler in the MouseArea trying to write to the myData field). However, this basic proof of concept doesn't work, but I don't really understand why.

    The error I get (on drag and release of the mouse button) is: qrc:///main.qml:29:TypeError:Type error

    Which matches up with the line "myData.a = mouse.x;", so it fails straight away.

    Any idea's where I'm going wrong? I've tried with the fields being int, double, QVariant, and qreal, none of which have worked. Is in a fundamental inability in QML to link objects like that? If so, any idea how, for example, anchors.fill is implemented in the Qt source code?