如何创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属性。
(我知道有QQmlListProperty
但QQmlListProperty
不支持局部刷新,总是需要重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任意属性的任意对象,并添加到模型中。 然后从模型中你有一个单一的object
angular色返回对象,你可以查询和使用该对象来检索它拥有的属性。 鉴于“经典”列表模型实现将定义具有静态,固定模式的模型,使用这种方法允许具有不同属性的模型中的“非晶”对象。
当然,这需要一些types安全性,例如对于这样的模型中的每个对象都有一个property int type
,并基于它可以确定对象的可用属性。 我通常的做法是为Loader
提供一个Loader
,然后让它将对象作为数据源传递给不同的QML UI实现,从而可视化实例化的对象types。 这样你就可以在模型中拥有两个不同的对象,而不同的QML项目就像视图代表一样。
做最终“所有交易的杰克”列表/模型对象的最后一步是为它实现QQmlListProperty
和Q_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
将可以访问,即使没有做任何事情转发它,但由于某种原因,它不适用于内联组件,与这样的组件唯一可能的方法是将其公开的属性装载机。