Как создать универсальную объектную модель для использования в 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)

630   1  

1 ответ:

//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
Да, это так, разве ты не пытался? Конечно, это будет не AnimalModel, а AnimalModel *, но пока модель наследует QAbstractListModel, это все, что вам нужно. Вам даже не нужна Часть NOTIFY, так как изменения, внутренние для модели, будут автоматически отражены в любом случае. modelAnimalsChanged имеет смысл только тогда, когда вы заменяете всю модель другой моделью и, естественно, закрываете предупреждения QML об использовании свойства без сигнала уведомления. Более чистый способ сделать последнее, когда объект модели не изменяется это просто вернуть a AnimalModel * из слота или A Q_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 или производные, естественно, включая QMLs QtObject, он может непосредственно использоваться в качестве модели для управления 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 и иметь фактическую ссылку на объект UI parent.object.prop, либо использовать setSource(name + ".qml", {"object": object}) и иметь свойство объекта непосредственно в этом элементе, однако setSource будет работать только с внешними источниками, а не с inline Components. обратите внимание, что в случае внешнего источника, object будет доступен даже без каких-либо действий по его пересылке, однако по какой-то причине он не работает со встроенными компонентами, с такими компонентами как свойство загрузчика.

Comments

    Ничего не найдено.