Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expose list of custom objects with Q_PROPERTY

Tags:

c++

qt

qt5

qml

I have a very simple class with 2 properties; key and value:

KeyValue.h:

class KeyValue : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QString key READ getKey WRITE setKey NOTIFY keyChanged)
  Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)

 public:
  KeyValue(const QString& key, const QString& value, QObject* parent = 0);  

 signals:
  void keyChanged();
  void valueChanged();

 private:
  QString _key;
  QString _value;

  QString getKey() const;
  QString getValue() const;

  void setKey(const QString& key);  
  void setValue(const QString& value);
};
Q_DECLARE_METATYPE(KeyValue)

In another class I would like a property containing a list of KeyValue objects, so I can use this list as a model in QML.

Controller.h

class Controller : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QList<KeyValue*> items READ getItems NOTIFY itemsChanged)

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

 signals:
  void itemsChanged(); 

 private:
  QList<KeyValue*> getItems() const;
};

I want to be able to use this in QML the following way:

import QtQuick 2.7
import customqml 1.0

Item{
  Controller{
    id: controller
  }
  Repeater{
    model: controller.items
    Text{
      text: modelData.key + ": " + modelData.value
    }
  }
}

Both classes are registered in my main.cpp file:

qmlRegisterType<KeyValue>("customqml", 1, 0, "KeyValue");
qmlRegisterType<Controller>("customqml", 1, 0, "Controller");

The above code does not work, bacause I apparently can't expose a QList to QML directly. I have tried using QAbstractItemModel and QQmlListProperty, but I was unable to get it to work. Can anyone point me in the right direction?

My primary issues are the type of the items property in the Controller class and the return value of the getItems method.

I'm using Qt 5.9 if that makes any difference.

like image 214
KMK Avatar asked Oct 16 '18 11:10

KMK


People also ask

What is Q_PROPERTY in QML?

Exposing Properties. A property can be specified for any QObject-derived class using the Q_PROPERTY() macro. A property is a class data member with an associated read function and optional write function. All properties of a QObject-derived or Q_GADGET class are accessible from QML.

What is QProperty in Qt?

QProperty<T> is one of the classes implementing Qt Bindable Properties. It is a container that holds an instance of T. You can assign a value to it and you can read it via the value() function or the T conversion operator.

What is QObject in QML?

The QtObject type is a non-visual element which contains only the objectName property.


1 Answers

Note:

  • The getters and setters are generally public except for exceptions so move it to the public part

  • The classes that inherit from QObject do not need QMetaType because when you want to transfer data of that class the pointers are used.

Not all data types are supported by QML through Q_PROPERTY, so a possible solution is to export through known classes such as

  • QList<QObject *>:

class Controller : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<QObject *> items READ getItems NOTIFY itemsChanged)
public:
    explicit Controller(QObject *parent = nullptr);
    QList<QObject *> getItems() const;
signals:
    void itemsChanged();
private:
    QList<KeyValue *>key_values_list;
};

...
QList<QObject *> Controller::getItems() const
{
    QObjectList l;
    for(auto e: key_values_list)
        l << e;
    return l;
}
  • QVariantList:

class Controller : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QVariantList items READ getItems NOTIFY itemsChanged)
public:
    explicit Controller(QObject *parent = nullptr);
    QVariantList getItems() const;
signals:
    void itemsChanged();
private:
    QList<KeyValue *>key_values_list;
};

...
QVariantList Controller::getItems() const
{
    QVariantList l;
    for(auto e: key_values_list)
        l.append(QVariant::fromValue(e));
    return l;
}

Other options is to implement a model, the following example shows only a read-only model:

keyvaluemodel.h

#ifndef KEYVALUEMODEL_H
#define KEYVALUEMODEL_H

#include "keyvalue.h"

#include <QAbstractListModel>

class KeyValueModel : public QAbstractListModel
{
    Q_OBJECT

public:
    explicit KeyValueModel(QObject *parent = nullptr)
        : QAbstractListModel(parent)
    {
        key_values_list = {new KeyValue{"k", "v"}, new KeyValue{"k2", "v2"}};
    }
    int rowCount(const QModelIndex &parent = QModelIndex()) const override
    {
        if (parent.isValid())
            return 0;
        return key_values_list.length();
    }
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
    {
        if (!index.isValid())
            return QVariant();
        if(index.row() >= 0 && index.row() < rowCount())
            return QVariant::fromValue(key_values_list[index.row()]);
        return QVariant();
    }
private:
    QList<KeyValue* >key_values_list;
};

#endif // KEYVALUEMODEL_H

class Controller : public QObject
{
    Q_OBJECT
    Q_PROPERTY(KeyValueModel* items READ getItems NOTIFY itemsChanged)
public:
    explicit Controller(QObject *parent = nullptr);
    KeyValueModel* getItems() const;
signals:
    void itemsChanged();
private:
    KeyValueModel *model;
};

...
Text{
    text: display.key + ": " + display.value
}
...

And in a similar way you can implement a QQmlListProperty, in the docs there are many examples.

like image 172
eyllanesc Avatar answered Oct 09 '22 17:10

eyllanesc