1
votes

I'm attempting to use a list property in Qml for an underlying C++ object. I would like to be able to read and write it. The documentation for QQmlListProperty says that only the READ function is implemented, and is used for reading and writing the object. I get the error message:

Invalid property assignment: "string" is a read-only property

when I try to run the app. Here are the relevant source files:

main.cpp

#include <QtGui/QGuiApplication>
#include <QtQml>
#include "qtquick2applicationviewer.h"

#include "A.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    qmlRegisterType<A>("TestQmlComponents", 1, 0, "A");

    QtQuick2ApplicationViewer viewer;
    viewer.setMainQmlFile(QStringLiteral("qml/TestQml/main.qml"));
    viewer.showExpanded();

    return app.exec();
}

main.qml:

import QtQuick 2.0
import TestQmlComponents 1.0

Rectangle {
    width: 360
    height: 360

    A {
        id: a
        string: [
            "a", "b", "c"
        ]
    }

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}

A.h:

#ifndef A_H
#define A_H

#include <QObject>
#include <QList>
#include <QString>
#include <QtQml/QQmlListProperty>

class A : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<QString> string READ stringList)

public:
    explicit A(QObject *parent = 0);

    static void addString(QQmlListProperty<QString> *list, QString *str);
    void addString(QString *str);
    static int stringCount(QQmlListProperty<QString> *list);
    int stringCount() const;
    QQmlListProperty<QString> stringList();
    static QString *getString(QQmlListProperty<QString> *list, int ix);
    QString *getString(int ix);

private:
    QList<QString *> _string;
};

#endif // A_H

A.cpp

#include "A.h"

A::A(QObject *parent) :
    QObject(parent)
{
}

void A::addString(QQmlListProperty<QString> *list, QString *str)
{
    A *obj = qobject_cast<A *>(list->object);

    if (obj)
       obj->addString(str);
}

void A::addString(QString *str)
{
    _string << str;
}

int A::stringCount(QQmlListProperty<QString> *list)
{
    A *obj = qobject_cast<A *>(list->object);

    if (obj)
        return obj->stringCount();

    return 0;
}

QString *A::getString(int ix)
{
    return _string.at(ix);
}

QString *A::getString(QQmlListProperty<QString> *list, int ix)
{
    A *obj = qobject_cast<A *>(list->object);

    if (obj)
        return obj->getString(ix);

    return NULL;
}

int A::stringCount() const
{
    return _string.count();
}

QQmlListProperty<QString> A::stringList()
{
    return QQmlListProperty<QString>(this, NULL, addString, stringCount, getString, NULL);
}

Any idea what I'm doing wrong?

1

1 Answers

2
votes

QQmlListProperty can only be used for lists of QObject-derived object pointers.

Since a QStringList is transparently exposed to JavaScript as an array type, you can expose such a property directly in two ways:

  1. As a Q_PROPERTY, with the caveat that modifications are expensive, since the entire string list is retrieved, then modified, then written back. For large lists this is slow.

    Q_PROPERTY(QStringList strings READ strings WRITE setStrings NOTIFY stringsChanged)
    Q_SIGNAL void stringsChanged(const QStringList &);
    ...
    QStringList strings() const { return m_strings; }
    void setStrings(const QStringList & strings ) {
      m_strings = strings;
      emit stringsChanged(m_strings);
    }
    
  2. As a reference returned from a Q_INVOKABLE method: the data is accessed directly.

    Q_INVOKABLE QStringList & strings() { return m_strings; }