简体   繁体   English

列表<T*>到 qml 数组

[英]QList<T*> to qml array

I have a class Bar which inherits from QObject.我有一个继承自 QObject 的类 Bar。 I want to have a QList of Bar pointers in class Foo and expose it in qml.我想在 Foo 类中有一个 Bar 指针的 QList 并在 qml 中公开它。

foo.h foo.h

#ifndef FOO_H
#define FOO_H

#include <QQuickItem>

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int x READ x)

public:
    explicit Bar(QObject *parent = nullptr)
        : QObject(parent), mX(123) {}
    virtual ~Bar() {}

    int x() const { return mX; }
private:
    int mX;
};

class Foo : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QList<QObject *> t1 READ t1)
    Q_PROPERTY(QList<Bar *> t2 READ t2)

public:
    explicit Foo(QQuickItem *parent = nullptr)
        : QQuickItem(parent) {
        mT1.append(new Bar(this));
        mT1.append(new Bar(this));

        mT2.append(new Bar(this));
        mT2.append(new Bar(this));
    }
    virtual ~Foo() {}

    QList<QObject *> t1() const { return mT1; }
    QList<Bar *> t2() const { return mT2; }

private:
    QList<QObject *> mT1;
    QList<Bar *> mT2;
};

#endif // FOO_H

qmlRegisterType("Custom.Foo", 1, 0, "Foo"); qmlRegisterType("Custom.Foo", 1, 0, "Foo");

main.qml主文件

import QtQuick 2.6
import QtQuick.Window 2.2
import Custom.Foo 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log(foo.t1)
            console.log(foo.t1.x)
            console.log(foo.t2)
            console.log(foo.t2.x)
        }
    }
    Foo {
        id: foo
    }
}

QList<QObject *> works I can access it's member x and console.log(foo.t1) outputs qml: [Bar(0x10e4be0),Bar(0x10bd6d0)] but QList<Bar *> doesn't. QList<QObject *>工作我可以访问它的成员xconsole.log(foo.t1)输出console.log(foo.t1) qml: [Bar(0x10e4be0),Bar(0x10bd6d0)]QList<Bar *>没有。 When I try to access member x I get qml: undefined and console.log(foo.t2) outputs qml: QVariant(QList<Bar*>) .当我尝试访问成员x我得到console.log(foo.t2) : undefined 和console.log(foo.t2)输出console.log(foo.t2) qml: QVariant(QList<Bar*>)

Why QList<Bar *> is exposed in QVariant?为什么QList<Bar *>在 QVariant 中暴露? Is there a way to expose it in qml same way QList<QObject *> ?有没有办法在 qml 中以同样的方式公开它QList<QObject *>

First off read the following article carefully:首先请仔细阅读以下文章:

In order to create JavaScript arrays or objects from C++ data structures, you should expose those of C++ in QVariantList (which gets converted to JavaScript array) or QVariantMap (which gets converted to JavaScript object).为了从 C++ 数据结构创建 JavaScript 数组或对象,您应该在QVariantList (转换为 JavaScript 数组)或QVariantMap (转换为 JavaScript 对象)中公开 C++ 的数组或对象。

Also section "Sequence Type to JavaScript Array" says:另外“JavaScript 数组的序列类型”部分说:

Certain C++ sequence types are supported transparently in QML as JavaScript Array types. QML 透明地支持某些 C++ 序列类型作为 JavaScript 数组类型。

In particular, QML currently supports:特别是,QML 目前支持:

 QList<int> QList<qreal> QList<bool> QList<QString> and QStringList QList<QUrl> QVector<int> QVector<qreal> QVector<bool>

In your case, note that:在您的情况下,请注意:

Other sequence types are not supported transparently, and instead an instance of any other sequence type will be passed between QML and C++ as an opaque QVariantList .不透明地支持其他序列类型,而是将任何其他序列类型的实例作为不透明的QVariantList在 QML 和 C++ 之间传递。

Therefore, you should prepare your list in a QVariantList .因此,您应该在QVariantList准备您的列表。 Also note that since both QVariantMap and QVariantList are of type QVariant , so you can insert a map into a list or vice versa, to create a complex array of objects or a complex object having a list inside.还要注意,由于既QVariantMapQVariantList是类型QVariant ,这样可以插入一个地图到一个列表,或反之亦然,创建对象的一系列复杂的或具有一个列表内一个复杂的对象。

QML Engine recognizes QList<QObject*> as a special case. QML 引擎将QList<QObject*>识别为特殊情况。 You can't expose a property of type QList<Bar*> , the engine doesn't understand it, and there is no way to register it as a new list type.您不能公开QList<Bar*>类型的属性,引擎不理解它,并且无法将其注册为新的列表类型。

Another option which hadn't been mentioned is casting the list ... as sinful as it may be considered by some.另一个没有提到的选项是投射列表……尽管有些人认为它是有罪的。

If you must have your data stored in QList<Bar*> then you can return it as a QList<QObject*> as below...如果您必须将数据存储在QList<Bar*>那么您可以将其作为QList<QObject*> ,如下所示...

QList<QObject *> t2() const {
    Q_ASSERT(sizeof(QObject*) == sizeof(Bar*));               // check pointers are the same size
    Q_ASSERT(sizeof(QList<QObject*>) == sizeof(QList<Bar*>)); // check lists are the same size
    Q_ASSERT(qobject_cast<Bar*>((QObject*)mT2.at(0)));        // check Bar is derived from QObject
    return *reinterpret_cast<const QList<QObject *>*>(&mT2);  // now cast the list
}

It's fast, easy, and all the object properties and Q_INVOKABLE methods of the Bar class are available in QML.它快速、简单,并且 Bar 类的所有对象属性和 Q_INVOKABLE 方法都可以在 QML 中使用。

I've added a few checks in just in case, but if you can be certain your class is derived from QObject then you might choose not to bother.我已经添加了一些检查以防万一,但是如果您可以确定您的类是从 QObject 派生的,那么您可能会选择不打扰。

QList<QObject*> support is documented here: http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qobjectlist-based-model . QList<QObject*>支持记录在此处: http : //doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qobjectlist-based-model It is no coincidence that it is a special case... it has been chosen to be a special ;) Each QObject* is copied from the QList and used to create a new javascript array inside the QML Engine.它是一种特殊情况并非巧合……它被选为特殊情况;) 每个QObject*都是从QList复制的,并用于在 QML 引擎内创建一个新的 javascript 数组。

Encompassing things inside a variant is fine, so long as the engine knows the type which is inside the variant .将事物包含在variant是可以的,只要引擎知道variant的类型即可。 You can register Bar type so that the QML engine could understand:您可以注册Bar类型,以便 QML 引擎可以理解:

  • QVariant(Bar*) and even QVariant(Bar*)甚至

  • QList<QVariant(Bar*)> . QList<QVariant(Bar*)>

However you can never get the engine to understand QList<Bar*> .但是,您永远无法让引擎理解QList<Bar*>

(note: if Bar derives from QObject , then there is no need to register it to QML, support will be implicit) (注意:如果Bar派生自QObject ,则无需将其注册到 QML,支持将是隐式的)

Other options... QQmlListProperty and QList<QVariant> will work, but have been covered in other answers.其他选项... QQmlListPropertyQList<QVariant>将起作用,但已在其他答案中介绍。 QAbstractListModel is another option, but there are already enough resources elsewhere to see how that is done. QAbstractListModel是另一种选择,但其他地方已经有足够的资源来了解它是如何完成的。

full code:完整代码:

foo.h foo.h

#ifndef FOO_H
#define FOO_H

#include <QQuickItem>

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int x READ x)

public:
    explicit Bar(QObject *parent = nullptr)
        : QObject(parent), mX(123) {}
    virtual ~Bar() {}

    int x() const { return mX; }
private:
    int mX;
};

class Foo : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QList<QObject *> t2 READ t2)

public:
    explicit Foo(QQuickItem *parent = nullptr)
        : QQuickItem(parent) {
        mT2.append(new Bar(this));
        mT2.append(new Bar(this));
    }
    virtual ~Foo() {}

    QList<QObject *> t2() const {
        Q_ASSERT(sizeof(QObject*) == sizeof(Bar*));               // check pointers are the same size
        Q_ASSERT(sizeof(QList<QObject*>) == sizeof(QList<Bar*>)); // check lists are the same size
        Q_ASSERT(qobject_cast<Bar*>((QObject*)mT2.at(0)));        // check Bar is derived from QObject
        return *reinterpret_cast<const QList<QObject *>*>(&mT2);  // now cast the list
    }

private:
    QList<Bar *> mT2;
};

#endif // FOO_H

main.cpp主程序

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "foo.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    qmlRegisterType<Foo>("Custom.Foo", 1, 0, "Foo");

    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml主文件

import QtQuick 2.6
import QtQuick.Window 2.2
import Custom.Foo 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log(foo.t2)
            console.log(foo.t2[0].x)
            console.log(foo.t2[1].x)
        }
    }
    Foo {
        id: foo
    }
}

To expose a list of QObject to Qml, Qt recommends to use QQmlListProperty .要将QObject列表公开给 Qml,Qt 建议使用QQmlListProperty

To create this object you need to override some of its access function, like size() , at() , etc.要创建这个对象,你需要覆盖它的一些访问函数,比如size()at()等。

You should have a look to你应该看看

Here is how I would do it using QQmlListProperty .这是我将如何使用QQmlListProperty做到这QQmlListProperty It takes awhile to get used to it, but it works exactly the way you'd expect.习惯它需要一段时间,但它的工作方式完全符合您的预期。

Foo.h foo.h

#ifndef FOO_H
#define FOO_H

#include <QQuickItem>

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int x READ x)

public:
    explicit Bar(QObject *parent = nullptr)
        : QObject(parent), mX(123) {}
    virtual ~Bar() {}

    int x() const { return mX; }
private:
    int mX;
};

class Foo : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Bar> bars READ bars)

public:
    explicit Foo(QQuickItem *parent = nullptr)
        : QQuickItem(parent) {
        mBars.append(new Bar(this));
        mBars.append(new Bar(this));
    }
    virtual ~Foo() {}

    QQmlListProperty<Bars> bars();
    void appendBar(Bar*);
    int barCount() const;
    Bar *bar(int) const;
    void clearBars();
    void replaceBar(int, Bar*);
    void removeLastBar();

private:
    QList<Bar *> mBars;
};

#endif // FOO_H

Foo.cpp文件

#include "Foo.h"

QQmlListProperty<Bar> Foo::bars()
{
    return {this, this,
            [](QQmlListProperty<Bar> *prop, Bar *newBar)
            {
                Foo *foo = qobject_cast<Foo*>(prop->object);
                if (foo) {
                    foo->appendBar(newBar);
                }
            },
            [](QQmlListProperty<Bar> *prop)->int
            {
                Foo *foo = qobject_cast<Foo*>(prop->object);
                if (foo ) {
                    return foo->barCount();
                }
                return 0;
            },
            [](QQmlListProperty<Bar> *prop, int index)->Bar*
            {
                Foo *foo = qobject_cast<Foo*>(prop->object);
                if (foo) {
                    return foo->bar(index);
                }
                return nullptr;
            },
            [](QQmlListProperty<Bar> *prop)
            {
                Foo *foo = qobject_cast<Foo*>(prop->object);
                if (foo) {
                    foo->clearBars();
                }
            },
            [](QQmlListProperty<Bar> *prop, int index, Bar* newBar)
            {
                Foo *foo = qobject_cast<Foo*>(prop->object);
                if (foo) {
                    foo->replaceBar(index, newBar);
                }
            },
            [](QQmlListProperty<Bar> *prop)
            {
                Foo *foo = qobject_cast<Foo*>(prop->object);
                if (foo) {
                    foo->removeLastBar();
                }
            }
    };
}

void Foo::appendBar(Bar* newBar) {
    mBars.append(newBar);
}

int Foo::barCount() const
{
    return mBars.count();
}

Bar *Foo::bar(int index) const
{
    return mBars.at(index);
}

void Foo::clearBars() {
    mBars.clear();
}

void Foo::replaceBar(int index, Bar *newBar)
{
    mBars[index] = newBar;
}

void Foo::removeLastBar()
{
    mBars.removeLast();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM