4
votes

I am new to QML and have a problem in accessing a property.property of a C++ object:

C++, frequency and station both are Qt metatype registered objects:

CStation *station = new CStation(...); // QObject
CFrequency *frequency = new CFrequency(..); // QObject
QQmlContext *qmlContext = viewer.rootContext();
qmlContext->setContextProperty("myatcstation", station);
qmlContext->setContextProperty("myfrequency", frequency);

QML:

 RowLayout { ....
        TextField {
            text: myatcstation.toQString(true)
        }
    }
 ....       text: myfrequency.toQString(true)

This works, but when I write: text: myatcstation.frequency.toQString(true) I do get TypeError: Object [object Object] has no method 'toQString'

frequency is a property of class CStation set as Q_PROPERTY(CFrequency frequency READ getFrequency)

Crosscheck in C++ works:

CFrequency test = station->property("frequency").value<CFrequency>();

-- Edit: Frank's answer --

Both classes are derived from QObject, and it is not as per textbook as they are made copyable. I am aware of the Identity vs value situation.

Basically frequency is a value object, but I have made it QObject based so I am able to use properties with it (see Any chance to use non QObject classes with QML ). In the example, toString is Q_INVOKABLE, frequency in the non-working case returns a copy of a QObject derived CFrequency object.

-- Edit: Further findings --

When I change the frequency property to return CFrequency* instead of CFrequency it does not work either. As I get TypeError: Cannot call method 'toQString' of undefined the situation seems to be the same. CFrequency alone works, but QML does not understand that myatcstation.frequency is a frequency object which has toString.

2

2 Answers

1
votes

CFrequency isn't a QObject I assume, otherwise you wouldn't return it by value but by pointer. To make `toQString() callable from QML, it must be either Q_INVOKABLE or a slot, which means that CFrequency must be a QObject as well.

If a station has only one frequency, consider moving the relevant information into the station object, i.e. add the frequency information you need as properties to CStation.

To get updates when the frequency changes, consider to use a property such as Q_PROPERTY(QString frequencyAsString READ frequencyAsString NOTIFY frequencyAsStringChanged) instead of toQString(). Properties have the update mechanism "built-in" via property bindings, while there's no good way to tell QML that it should call toQString again because the frequency changed.

1
votes

I solved a similar problem:

class TopObject : public QObject
{
  Q_OBJECT
    Q_PROPERTY(ValueObject* theValue ...
  ...
}

class ValueObject : public QObject
{
  Q_OBJECT
    Q_PROPERTY(QString value ...
  ...
}

In the main application:

qRegisterMetaType<ValueObject>("ValueObject");
qmlRegisterType<ValueObject>("com.name.comp", 1, 0, "ValueObject");

...->setContextProperty("topObject", new TopObject());

And in the qml code:

import com.name.comp 1.0

... {
  text: topObject.theValue.value
...

It needed both, returning the property (ValueObject) as pointer and registering it with qmlRegisterType.

See also Exchange Data and Objects between C++ and QML and vice versa