【问题标题】:How to expose list of custom objects with Q_PROPERTY如何使用 Q_PROPERTY 公开自定义对象列表
【发布时间】:2018-10-16 11:52:40
【问题描述】:

我有一个非常简单的类,有 2 个属性;键和值:

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)

在另一个类中,我想要一个包含 KeyValue 对象列表的属性,因此我可以将此列表用作 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;
};

我希望能够通过以下方式在 QML 中使用它:

import QtQuick 2.7
import customqml 1.0

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

这两个类都在我的 main.cpp 文件中注册:

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

上面的代码不起作用,因为我显然不能将 QList 直接暴露给 QML。我曾尝试使用 QAbstractItemModel 和 QQmlListProperty,但我无法让它工作。谁能指出我正确的方向?

我的主要问题是控制器类中 items 属性的类型和 getItems 方法的返回值。

如果这有什么不同的话,我正在使用 Qt 5.9。

【问题讨论】:

  • 您可以使用QVariantList。但既然你想在 QML 中使用它作为模型,你应该使用QAbstractListModel-descendent。 QQmlObjectListModel 使用起来非常舒服,您可以在此处找到:github.com/Cavewhere/lib-qt-qml-tricks/tree/master/include 免责声明:我不是作者,只是一个用户。Q_DECLARE_METATYPE(KeyValueList) 和相应的 typedef 之后也可能会这样做,但是我可能错了。
  • 请阅读doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html你可以使用QObjectList或QVariantList,或子类QAbstractItemModel。
  • 感谢@derM 的链接。我没有直接使用那个模型,但是源代码给了我很大的启发

标签: c++ qt qml qt5


【解决方案1】:

注意:

  • getter 和 setter 通常是公开的,除非有例外,所以将其移至公开部分

  • 从 QObject 继承的类不需要 QMetaType,因为当您要传输该类的数据时,会使用指针。

不是所有的数据类型都被 QML 通过 Q_PROPERTY 支持,所以一个可能的解决方案是通过已知的类例如导出

  • QList&lt;QObject *&gt;

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;
}

其他选项是实现一个模型,下面的例子只显示了一个只读模型:

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
}
...

您可以通过类似的方式实现 QQmlListProperty,在文档中有很多示例。

【讨论】:

  • 子类化 QAbstractListModel 正是我所需要的 :)
【解决方案2】:

如果您想要一个具有添加/删除对象和更改数据的复杂模型,您应该研究子类化QAbstractListModel

作为一种简单但不太灵活的方式,您可以使用QVariantList 并使您的 Controller 类成为值类型。你需要:

  • Controller 头文件末尾的宏 Q_DECLARE_METATYPE(Controller)。
  • Controller 的复制构造函数
  • Controller 的默认构造函数

Q_PROPERTY 类型和返回值则为 QVariantList。

【讨论】:

  • Controller 设为基于值的类是没有意义的。这样就不可能有信号了。如果不需要更改信号,KeyValue 很可能是Q_GADGET
  • 这就是为什么主要建议是子类化 QAbstractListModel。对于不更改数据或信号的对象,值类型可能会有所帮助。我还不知道Q_GADGET,谢谢提示。
猜你喜欢
  • 2011-12-22
  • 1970-01-01
  • 2019-10-04
  • 1970-01-01
  • 2014-01-08
  • 1970-01-01
  • 1970-01-01
  • 2019-11-08
  • 1970-01-01
相关资源
最近更新 更多