如何创建一个通用对象模型用于QML? [英] How to create a generic object model for use in QML?

查看:152
本文介绍了如何创建一个通用对象模型用于QML?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to know if there is any macro or way how to register Qt model as property of QObject.

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

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

I Know I can pass it to root context of QuickView

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

如果我通过Qml宏注册了QObject,我可以传递此对象: p>

In case I have QObject registered via Qml macros, I can pass this object to view too:

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

但是如果我想要QObject保存任何数据的模型?

But what If I want to have QObject which holds model of any data?

例如:

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属性。

Every example I found until now shows how to pass QAbstractListModel to root context. But none how to use it as QObject property.

(我知道有 QQmlListProperty ,但

(I know there is QQmlListProperty but QQmlListProperty doesn't support partial refresh. It's always necessary to rebuild all Qml objects)

推荐答案

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

是的,你试试吗?当然,它不会是 AnimalModel ,而是 AnimalModel * ,但只要模型继承 QAbstractListModel ,这就是你需要的。你甚至不需要 NOTIFY 部分,因为模型内部的更改将自动反映。 modelAnimalsChanged 只有在用不同的模型替换整个模型时才有意义,并且自然地,关闭使用没有通知信号的属性的QML警告。当模型对象不改变时,更简单的方法是从槽中返回 AnimalModel * 或者从槽中返回 Q_INVOKABLE

Yes it is, didn't you try? Of course, it will not be a AnimalModel but a AnimalModel *, but as long as the model inherits QAbstractListModel, that's all you need. You don't even need the NOTIFY part, as changes, internal to the model will be automatically reflected anyway. modelAnimalsChanged only makes sense when you replace the entire model with a different model, and naturally, to shut up QML's warnings about using a property without a notify signal. A cleaner way to do the latter when the model object doesn't change is to just return a AnimalModel * from a slot or a Q_INVOKABLE.

如果你想要一个真正灵活的模型,你可以创建一个存储 QObject * 然后从QML可以创建任意对象与任意属性,并添加到模型。然后从模型中你有一个对象角色,它返回对象,你可以查询和使用对象来检索它所拥有的属性。而经典列表模型实现将定义具有静态,固定模式的模型,使用该方法允许在模型中具有不同属性的无定形对象。

If you want a truly flexible model, you can make one that stores QObject *, then from QML you can create arbitrary objects with arbitrary properties, and add to the model. Then from the model you have a single object role which returns the object, and you can query and use the object to retrieve the properties it holds. Whereas a "classical" list model implementation will define a model with a static, fixed schema, using this approach allows to have "amorphous" objects in the model with different properties.

当然,这需要某种类型的安全性,例如对于这样的模型中的每个对象都有一个属性int类型,并且基于它,你可以确定对象的可用属性。我通常的方法是有一个 Loader 为一个委托,并将它作为一个数据源的对象传递给不同的QML UI实现,可视化它实例化的对象类型。这种方式你在模型中有两个不同的对象,并且不同的QML项目作为视图代理。

Naturally, this requires some type safety, for example have a property int type for each object in such a model, and based on it you can determine the available properties for the object. My usual approach is to have a Loader for a delegate, and have it pass the object as a data source to different QML UI implementations visualizing that object type that it instantiates. This way you have both different objects in the model, and different QML items as view delegates.

最后一步做出最终的所有行业的杰克列表/模型对象是为它实现 QQmlListProperty Q_CLASSINFO(DefaultProperty,container)动态列表/模型,或使用QML的声明性语法。还要注意,使用此解决方案,您可以添加或删除此类模型,甚至删除声明式实例化的对象。

The last step to making the ultimate "jack of all trades" list/model object is to implement QQmlListProperty and Q_CLASSINFO("DefaultProperty", "container") for it, allowing you to both compose the list/model dynamically, or using QML's declarative syntax. Also note that with this solution, you can add to or remove from such a model, even remove declaratively instantiated objects.

此外,根据您的使用场景,为 qmlRegisterType() qmlRegisterUncreatableType()

Also, depending on your usage scenario, you may have to either qmlRegisterType() or qmlRegisterUncreatableType() for the model.

OK,乍一看,它看起来像任何数据的模型,你不意味着无模式模型,而只是不同的模式模型。在这种情况下,您可以使用 QAbstractListModel * 或甚至,而不是返回 AnimalModel * c> QObject * - 它将工作在QML无论如何,因为它采用动力学通过元系统。但无论如何,无模式模型是更强大和更灵活,它们不需要定义C ++代码,它可以单独从QML工作。

OK, on a second glance, it looks like by "model of any data" you didn't mean schema-less models but simply different schema models. In that case, instead of returning an AnimalModel *, you can use a QAbstractListModel * or even a QObject * - it will work in QML anyway, as it employs dynamism through the meta system. But at any rate, schema-less models are that much more powerful and flexible, and they don't need C++ code to be defined, it can all work from QML alone.

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 0,List); 你可以使用它几乎任何方式 - 它将保存任何 QObject QtObject 它可以直接用作驱动 ListView 的模型。您可以使用槽或声明式动态填充它,如下所示:

Then, after you qmlRegisterType<List>("Core", 1, 0, "List"); you can use it pretty much any way you want to - it will hold any QObject or derived, naturally including QMLs QtObject It can directly be used as a model to drive a ListView. You can populate it dynamically using the slots or declarative, like this:

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

它还将处理对象所有权,您可以轻松地将其嵌套,从本质上生成一个分区树模型 - 不能用QML的 ListModel 声明性地做。你可能想要添加一个 parentChanged 信号,并实现一个setter,如果你想绑定一个变化的父对象,在我的情况下没有必要。

It will also handle object ownership, and you can easily nest it, producing in essence a compartmentalized tree model - note that you can't declaratively do that with QML's ListModel. You may want to add a parentChanged signal and implement a setter that emits it if you want to bind against a changing parent, it was not necessary in my case.

如何在视图中使用它,你可以使用 objectName 属性或 int类型属性,并为委托使用 Loader

As of how to use it with a view, you can either use the objectName property or an int type property, and use a Loader for the delegate:

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

如果你使用对象名,你使加载器创建一个 name.qml 文件,如果你使用一个int,你可以创建一个数组 Component ,并使用适当的索引作为源组件。您可以将对象作为 Loader 的属性,并让实际对象UI引用 parent.object.prop ,或者可以使用 setSource(name +.qml,{object:object})有对象属性直接进入该项目,然而 setSource 将只适用于外部源,而不是inline 组件。注意,在外部源的情况下, object 将是可访问的,即使没有做任何事情转发它,但是由于某些原因,它不工作与内联组件,组件,唯一可能的方法是将其作为加载器的属性。

If you use the object name, you make the loader create a name.qml file, if you use an int, you can create an array of Components and use the appropriate index as a source component. You can either expose the object as a property of the Loader and have the actual object UI reference it parent.object.prop, or you can use setSource(name + ".qml", {"object": object}) and have the object property directly into that item, however setSource will only work with external sources, not with inline Components. Note that in the case of an external source, object will be accessible even without doing anything to forward it, however for some reason it doesn't work with inline components, with such components the only possible way is to expose it as a property of the loader.

这篇关于如何创建一个通用对象模型用于QML?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆