Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QML: Type Error with custom QObject

Tags:

c++

qt

qml

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?

like image 440
Doug Avatar asked Dec 25 '22 04:12

Doug


1 Answers

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.
like image 63
Mitch Avatar answered Dec 29 '22 07:12

Mitch