Как создать универсальную объектную модель для использования в QML?
Я хотел бы знать, есть ли какой-либо макрос или способ, как зарегистрировать модель Qt как свойство QObject.
Например, у меня есть AnimalModel (http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel).
Я знаю, что могу передать его в корневой контекст QuickView
QuickView view;
view.rootContext()->setContextProperty("myModel", &model);
В случае, если у меня есть QObject, зарегистрированный с помощью макросов Qml, я могу передать этот объект для просмотра:
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 не поддерживает частичное обновление. Всегда необходимо перестроить все объекты Qml)
1 ответ:
Да, это так, разве ты не пытался? Конечно, это будет не//Is this possible in any way? //Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)AnimalModel, аAnimalModel *, но пока модель наследуетQAbstractListModel, это все, что вам нужно. Вам даже не нужна ЧастьNOTIFY, так как изменения, внутренние для модели, будут автоматически отражены в любом случае.modelAnimalsChangedимеет смысл только тогда, когда вы заменяете всю модель другой моделью и, естественно, закрываете предупреждения QML об использовании свойства без сигнала уведомления. Более чистый способ сделать последнее, когда объект модели не изменяется это просто вернуть aAnimalModel *из слота или AQ_INVOKABLE.Если вы хотите действительно гибкую модель, вы можете сделать такую, которая хранит
Естественно, для этого требуется некоторый тип безопасности, например иметьQObject *, затем из QML вы можете создавать произвольные объекты с произвольными свойствами и добавлять в модель. Затем из модели у вас есть одна рольobject, которая возвращает объект, и вы можете запросить и использовать объект для получения свойств, которые он содержит. В то время как "классическая" реализация модели списка будет определять модель со статической, фиксированной схемой, используя это подход позволяет иметь в модели "аморфные" объекты с различными свойствами.property int typeдля каждого объекта в такой модели, и на его основе можно определить доступные свойства для объекта. Мой обычный подход состоит в том, чтобы иметьLoaderдля делегата и передать объект в качестве источника данных в различные реализации пользовательского интерфейса QML, визуализирующие тот тип объекта, который он создает. Таким образом, у вас есть оба разных объекты в модели и различные элементы QML в качестве делегатов представления.Последним шагом к созданию конечного объекта list / model "jack of all trades" является реализация
QQmlListPropertyиQ_CLASSINFO("DefaultProperty", "container")для него, что позволяет вам как составлять список/модель динамически, так и использовать декларативный синтаксис QML. Также обратите внимание, что с помощью этого решения вы можете добавлять или удалять из такой модели, даже удалять декларативно созданные объекты.Кроме того, в зависимости от сценария использования вам может потребоваться либо
Хорошо, на второй взгляд, похоже, что под "моделью любых данных" вы имели в виду не модели без схем, а просто разные модели схем. В этом случае вместо возвратаqmlRegisterType()илиqmlRegisterUncreatableType()для модели.AnimalModel *Вы можете использоватьQAbstractListModel *или дажеQObject *- это будет работать в QML в любом случае, поскольку он использует динамизм через метасистему. Но в любом случае, модели без схем намного мощнее и гибче, и им не нужен код 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или производные, естественно, включая QMLsQtObject, он может непосредственно использоваться в качестве модели для управленияListView. Вы можете заполнить его динамически, используя слоты или декларативно, например:List { QtObject { ... } QtObject { ... } List { QtObject { ... } QtObject { ... } } }Он также будет обрабатывать владение объектами, и вы можете легко вложить его, создав, по сути, разделенную модель дерева - обратите внимание, что вы не можете декларативно сделать это с помощью QML
ListModel. Вы можете добавить сигналparentChangedи реализовать сеттер, который его выдает, Если вы хотите связать с изменяющимся родителем, в моем случае это не было необходимо.Как использовать его с представлением, вы можете использовать либо свойство
objectName, либо свойствоint typeи использоватьLoaderдля делегата:Loader { width: childrenRect.width height: childrenRect.height }Если вы используете имя объекта, вы заставляете загрузчик создать файл
name.qml, Если вы используете int, вы можете создать массивComponents и использовать соответствующий индекс в качестве исходного компонента. Вы можно либо выставитьobjectкак свойствоLoaderи иметь фактическую ссылку на объект UIparent.object.prop, либо использоватьsetSource(name + ".qml", {"object": object})и иметь свойство объекта непосредственно в этом элементе, однакоsetSourceбудет работать только с внешними источниками, а не с inlineComponents. обратите внимание, что в случае внешнего источника,objectбудет доступен даже без каких-либо действий по его пересылке, однако по какой-то причине он не работает со встроенными компонентами, с такими компонентами как свойство загрузчика.
Comments