如何使用 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<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;
其他选项是实现一个模型,下面的例子只显示了一个只读模型:
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?