将 QQmlListProperty 作为参数从 QML 传递到 C++

2024-03-17

我有一个 QML ProviderItem,它的对象属性返回 QObject 派生对象的列表。
我想将此列表传递给另一个 QML ConsumerItem 作为其函数 ConsumerAll 的属性。问题是我总是得到空的 QQmlListProperty ,所有回调函数设置为 0 且数据指针设置为 0 (我认为这些是默认构造的值)

像这样的事情:

ProviderItem.h

class ProviderItem : public QObject, public QQmlParserStatus
{
    Q_OBJECT
public:
    Q_PROPERTY(QQmlListProperty<QObject> objects READ objects NOTIFY objectsChanged)

    QQmlListProperty<QObject> objects();
    static int objects_count(QQmlListProperty<QObject> *);
    static QObject* objects_at(QQmlListProperty<QObject> *, int);

private:
    QList<QObject*> m_objects;
}

ProviderItem.cpp

QQmlListProperty<QObject> ProviderItemPrivate::objects()
{
    return QQmlListProperty<QObject>(this, nullptr,
         ProviderItem::objects_count,
         ProviderItem::objects_at);
}

QObject* ProviderItem::objects_at(QQmlListProperty<QObject> *prop, int index)
{
    ProviderItem* provider = qobject_cast<ProviderItem*>(prop->object)
    return provider->m_objects.at(index);
}

int ProviderItem::objects_count(QQmlListProperty<QObject> *prop)
{
    ProviderItem* provider = qobject_cast<ProviderItem*>(prop->object)
    return provider->m_objects.count();
}

消费者项目.h

class ConsumerItem: public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE void consumeAll(QQmlListProperty<QObject> obj);
};

消费者项目.cpp

void ConsumerItem::consumeAll(QQmlListProperty<QObject> obj)
{
    qDebug() << obj.count(); // thows exeption as count callback is 0
}

main.qml

Provider {
    id: objectProvider
}

Consumer {
    id: objectConsumer
}

Connections {
    target: objectProvider
    onObjectsChanged: {
        console.debug(objectProvider.objects)        // gives [object Object]
        objectConsumer.consumeAll(objectProvider.objects)

        var test = objectProvider.objects
        console.debug(test)                          // gives [object Object]
        Thermonav.testList(objectProvider.objects)
    }
}

显然 ProviderItem 和 ConsumerItem 已注册:

main.cpp

qmlRegisterType<ProviderItem>(uri, major, minor, "Provider");
qmlRegisterType<ConsumerItem>(uri, major, minor, "Consumer");

我也尝试过:

Q_INVOKABLE void consumeAll(QVariantMap obj);
Q_INVOKABLE void consumeAll(QQmlListProperty<QObject> obj);
Q_INVOKABLE void consumeAll(void* p);
Q_INVOKABLE void consumeAll(QVariant p);

但每次我得到默认构造的值。

根据本文 https://doc.qt.io/qt-5/qml-list.html:

与 C++ 集成时,请注意,从 C++ 传递到 QML 的任何 QQmlListProperty 值都会自动转换为列表值,反之亦然。

所以 qml [object Object] 中的输出对我来说看起来是合法的,因为“list”不是 js 数据类型。但它也说 QML 列表应该转换回 QQmlListProperty ,这绝对不适合我(或者我做错了)。

我正在使用 Qt 5.12.0

那么如何将 C++ 中创建的 QQmlListProperty 传递到 QML 列表,然后传递到 C++ 中的 QQmlListProperty 呢?


如果你使用QVariant并打印:

class ConsumerItem: public QObject
{
    Q_OBJECT
public:
    using QObject::QObject;
    Q_INVOKABLE void consumeAll(QVariant objects){
        qDebug() << objects;
    }
};

You get:

QVariant(QQmlListReference, )

所以解决方案是使用QQmlListReference https://doc.qt.io/qt-5/qqmllistreference.html:

class ConsumerItem: public QObject
{
    Q_OBJECT
public:
    using QObject::QObject;
    Q_INVOKABLE void consumeAll(const QQmlListReference & objects){
        qDebug() << objects.count();
    }
};

完整代码:

main.cpp

#include <QtQml>
#include <QtGui>
class Product: public QObject{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
    Product(const QString & name="", QObject* parent=nullptr):
        QObject(parent), m_name(name){}
    QString name() const{return m_name;}
    void setName(const QString &name){
        if(m_name == name) return;
        m_name = name;
        Q_EMIT nameChanged(m_name);
    }
    Q_SIGNAL void nameChanged(const QString &);
private:
    QString m_name;
};

class ProviderItem: public QObject{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Product> products READ products NOTIFY productsChanged)
public:
    using QObject::QObject;
    QQmlListProperty<Product> products(){
        return QQmlListProperty<Product>(this, this,
                                         &ProviderItem::appendProduct,
                                         &ProviderItem::productCount,
                                         &ProviderItem::product,
                                         &ProviderItem::clearProducts);
    }
    void appendProduct(Product* p) {
        m_products.append(p);
        Q_EMIT productsChanged();
    }
    int productCount() const{return m_products.count();}
    Product *product(int index) const{ return m_products.at(index);}
    void clearProducts() {
        m_products.clear();
        Q_EMIT productsChanged();
    }
    Q_SIGNAL void productsChanged();
private:
    static void appendProduct(QQmlListProperty<Product>* list, Product* p) {
        reinterpret_cast<ProviderItem* >(list->data)->appendProduct(p);
    }
    static void clearProducts(QQmlListProperty<Product>* list) {
        reinterpret_cast<ProviderItem* >(list->data)->clearProducts();
    }
    static Product* product(QQmlListProperty<Product>* list, int i) {
        return reinterpret_cast<ProviderItem* >(list->data)->product(i);
    }
    static int productCount(QQmlListProperty<Product>* list) {
        return reinterpret_cast<ProviderItem* >(list->data)->productCount();
    }
    QVector<Product *> m_products;
};

class ConsumerItem: public QObject
{
    Q_OBJECT
public:
    using QObject::QObject;
    Q_INVOKABLE void consumeAll(const QQmlListReference & products){
        for(int i=0; i<products.count(); ++i){
            if(Product *product = qobject_cast<Product *>(products.at(i))){
                qDebug()<< product->name();
            }
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    qmlRegisterType<Product>("foo", 1, 0, "Product");
    qmlRegisterType<ProviderItem>("foo", 1, 0, "Provider");
    qmlRegisterType<ConsumerItem>("foo", 1, 0, "Consumer");
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}
#include "main.moc"

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import foo 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    function create_product(){
        var product = Qt.createQmlObject('import foo 1.0; Product {}',
                                         provider,
                                         "dynamicSnippet1");
        product.name = "product"+provider.products.length;
        provider.products.push(product)
    }
    Timer {
        interval: 1000; running: true; repeat: true
        onTriggered: create_product()
    }
    Provider{
        id: provider
        onProductsChanged: consumer.consumeAll(provider.products)
        products: [
            Product{name: "product0"},
            Product{name: "product1"},
            Product{name: "product2"}
        ]
    }
    Consumer{
        id: consumer
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 QQmlListProperty 作为参数从 QML 传递到 C++ 的相关文章

随机推荐