如何使用 Q_PROPERTY 公开自定义对象列表

Posted

技术标签:

【中文标题】如何使用 Q_PROPERTY 公开自定义对象列表【英文标题】:How to expose list of custom objects with 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 的链接。我没有直接使用那个模型,但是源代码给了我很大的启发 【参考方案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,谢谢提示。

以上是关于如何使用 Q_PROPERTY 公开自定义对象列表的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Q_PROPERTY 向 QML 公开指向 Q_GADGET 的指针

如何有效地将单个对象的多个 C++ 数据成员公开给 QML?

在 QML 中使用自定义类中的 Q_PROPERTY

如何将 Qt 属性与自定义类一起使用?

Automapper 自定义解析器源成员到目标对象列表映射问题

如何使用 java 流过滤自定义对象列表? [复制]