如何创build用于QML的通用对象模型?

我想知道是否有任何macros或方式如何注册Qt模型作为QObject的属性。

例如,我有AnimalModel ( http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel )。

我知道我可以将它传递给QuickView的根上下文

 QuickView view; view.rootContext()->setContextProperty("myModel", &model); 

如果我有通过Qmlmacros注册的QObject,我也可以通过这个对象来查看:

 view.rootContext()->setContextProperty("obj", pDataObject); 

但是如果我想要拥有包含任何数据模型的QObject?

例如:

 class DataObject : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) ... AnimalModel m_modelAnimals; //Is this possible in any way? //Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged) }; 

我发现的每个例子都显示了如何将QAbstractListModel传递给根上下文。 但是没有一个如何使用它作为QObject属性。

(我知道有QQmlListPropertyQQmlListProperty不支持局部刷新,总是需要重build所有的Qml对象)

 //Is this possible in any way? //Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged) 

是的,你不试试吗? 当然,它不会是一个AnimalModel而是一个AnimalModel * ,但只要模型inheritance了QAbstractListModel ,这就是你所需要的。 您甚至不需要NOTIFY部分,因为模型内部的更改将自动反映出来。 modelAnimalsChanged只有在用不同的模型replace整个模型时才有意义,当然,closuresQML有关使用没有通知信号的属性的警告。 当模型对象没有改变时,更简单的方法是从一个槽或一个Q_INVOKABLE返回一个AnimalModel *

如果你想要一个真正灵活的模型,你可以创build一个存储QObject * ,然后从QML中创build任意属性的任意对象,并添加到模型中。 然后从模型中你有一个单一的objectangular色返回对象,你可以查询和使用该对象来检索它拥有的属性。 鉴于“经典”列表模型实现将定义具有静态,固定模式的模型,使用这种方法允许具有不同属性的模型中的“非晶”对象。

当然,这需要一些types安全性,例如对于这样的模型中的每个对象都有一个property int type ,并基于它可以确定对象的可用属性。 我通常的做法是为Loader提供一个Loader ,然后让它将对象作为数据源传递给不同的QML UI实现,从而可视化实例化的对象types。 这样你就可以在模型中拥有两个不同的对象,而不同的QML项目就像视图代表一样。

做最终“所有交易的杰克”列表/模型对象的最后一步是为它实现QQmlListPropertyQ_CLASSINFO("DefaultProperty", "container") ,允许你dynamic地组合列表/模型,或者使用QML的声明句法。 还要注意,使用这个解决scheme,你可以添加或删除这样一个模型,甚至删除声明性实例化的对象。

另外,根据您的使用场景,您可能必须为模型使用qmlRegisterType()qmlRegisterUncreatableType()

好吧,乍一看,它看起来像“任何数据的模型”,你不是指无模式模型,而只是不同的模式模型。 在这种情况下,您可以使用QAbstractListModel *或者甚至是QObject * ,而不是返回AnimalModel * ,无论如何,它都可以在QML中工作,因为它通过元系统使用dynamic。 但无论如何,无模式模型更加强大和灵活,并且不需要定义C ++代码,它们都可以单独使用QML。

 class List : public QAbstractListModel { Q_OBJECT QList<QObject *> _data; Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(QQmlListProperty<QObject> content READ content) Q_PROPERTY(QObject * parent READ parent WRITE setParent) Q_CLASSINFO("DefaultProperty", "content") public: List(QObject *parent = 0) : QAbstractListModel(parent) { } int rowCount(const QModelIndex &p) const { Q_UNUSED(p) return _data.size(); } QVariant data(const QModelIndex &index, int role) const { Q_UNUSED(role) return QVariant::fromValue(_data[index.row()]); } QHash<int, QByteArray> roleNames() const { static QHash<int, QByteArray> * pHash; if (!pHash) { pHash = new QHash<int, QByteArray>; (*pHash)[Qt::UserRole + 1] = "object"; } return *pHash; } int size() const { return _data.size(); } QQmlListProperty<QObject> content() { return QQmlListProperty<QObject>(this, _data); } public slots: void add(QObject * o) { int i = _data.size(); beginInsertRows(QModelIndex(), i, i); _data.append(o); o->setParent(this); sizeChanged(); endInsertRows(); } void insert(QObject * o, int i) { beginInsertRows(QModelIndex(), i, i); _data.insert(i, o); o->setParent(this); sizeChanged(); endInsertRows(); } QObject * take(int i) { if ((i > -1) && (i < _data.size())) { beginRemoveRows(QModelIndex(), i, i); QObject * o = _data.takeAt(i); o->setParent(0); sizeChanged(); endRemoveRows(); return o; } else qDebug() << "ERROR: take() failed - object out of bounds!"; return 0; } QObject * get(int i) { if ((i > -1) && (i < _data.size())) return _data[i]; else qDebug() << "ERROR: get() failed - object out of bounds!"; return 0; } signals: void sizeChanged(); }; 

然后,你qmlRegisterType<List>("Core", 1, 0, "List"); 你可以使用它几乎任何你想要的方式 – 它将容纳任何QObject或派生的,自然包括QML QtObject它可以直接用作模型来驱动一个ListView 。 您可以使用插槽或声明来dynamic填充它,如下所示:

 List { QtObject { ... } QtObject { ... } List { QtObject { ... } QtObject { ... } } } 

它也会处理对象的所有权,你可以很容易地嵌套它,从本质上产生一个分隔的树模型 – 注意,你不能用QML的ListModel声明地做到这一点。 你可能想要添加一个parentChanged信号,并实现一个setter,如果你想绑定一个不断变化的父对象,这对我来说不是必须的。

至于如何在视图中使用它,你可以使用objectName属性或者一个int type属性,并且为这个委托使用一个Loader

 Loader { width: childrenRect.width height: childrenRect.height } 

如果使用对象名称,则使加载器创build一个name.qml文件,如果使用int,则可以创build一个Component数组,并使用适当的索引作为源组件。 您可以将object作为Loader的属性公开,并将实际的对象UI引用为parent.object.prop ,也可以使用setSource(name + ".qml", {"object": object}) ,对象属性直接放入该项中,但是setSource只能与外部源一起使用,而不能与内联Component 。 请注意,在外部来源的情况下, object将可以访问,即使没有做任何事情转发它,但由于某种原因,它不适用于内联组件,与这样的组件唯一可能的方法是将其公开的属性装载机。