【问题标题】:Pass custom C++ object to Qml error (no properties)将自定义 C++ 对象传递给 Qml 错误(无属性)
【发布时间】:2015-03-05 07:49:34
【问题描述】:

我正在尝试编写代码,通过信号将一些数据从 C++ 引擎传递到 Qml 脚本,但看起来我做错了一些事情,因为当我在 Qml 中接收信号时,我的对象没有任何方法或特性!看看那个代码:

Signaller - 调用信号的类: signaller.h:

class Signaller : public QObject
{
    Q_OBJECT
public:
    explicit Signaller(QObject *parent = 0);
    Q_INVOKABLE void invokeSignal();
signals:
    void mysignal(TestClass test);
public slots:

};

signaller.cpp:

Signaller::Signaller(QObject *parent) :
    QObject(parent)
{
}

void Signaller::invokeSignal()
{
    TestClass s;
    emit mysignal(s);
}

TestClass - 将传递给 Qml 引擎的类,并且在 Qml 脚本中必须有测试方法:

测试.h:

class TestClass : public QObject
{
    Q_OBJECT
public:
    explicit TestClass(QObject *parent = 0);
             TestClass(const TestClass& obj);
             ~TestClass();
     Q_INVOKABLE void test();

signals:

public slots:

};

Q_DECLARE_METATYPE(TestClass)

Test.cpp:

TestClass::TestClass(QObject *parent) :
    QObject(parent)
{
    qDebug()<<"TestClass::TestClass()";
}

TestClass::TestClass(const TestClass &obj) :
    QObject(obj.parent())
{
    qDebug()<<"TestClass::TestClass(TestClass &obj)";
}


TestClass::~TestClass()
{
    qDebug()<<"TestClass::~TestClass()";
}

void TestClass::test()
{
    qDebug()<<"TestClass::test";
}

这两个类也注册在主函数中:

int main(int argc, char *argv[])
{
    // SailfishApp::main() will display "qml/template.qml", if you need more
    // control over initialization, you can use:
    //
    //   - SailfishApp::application(int, char *[]) to get the QGuiApplication *
    //   - SailfishApp::createView() to get a new QQuickView * instance
    //   - SailfishApp::pathTo(QString) to get a QUrl to a resource file
    //
    // To display the view, call "show()" (will show fullscreen on device).

    qmlRegisterType<TestClass>("Test", 1, 0, "Test");
    qmlRegisterType<Signaller>("Signaller", 1, 0, "Signaller");

    return SailfishApp::main(argc, argv);
}

这是我的带有测试代码的 Qml 文件:

import Signaller 1.0

Page {

    Signaller {
        id: sig
        Component.onCompleted: sig.invokeSignal()
        onMysignal: {
            console.log("signal",typeof test);
            console.log(typeof test.test);
        }
    }
}

还有日志:

[D] TestClass::TestClass:6 - TestClass::TestClass() 
[D] TestClass::TestClass:12 - TestClass::TestClass(TestClass &obj) 
[D] TestClass::TestClass:12 - TestClass::TestClass(TestClass &obj) 
[D] onMysignal:41 - signal object
[D] onMysignal:42 - undefined
[D] TestClass::~TestClass:18 - TestClass::~TestClass() 
[D] TestClass::~TestClass:18 - TestClass::~TestClass() 

从日志中可以看出,TestClass.test 字段在传递给 Qml 后为空。 我做错了什么?

【问题讨论】:

    标签: c++ qt qml


    【解决方案1】:

    您正在通过信号/插槽系统按值传递 QObject 派生对象 - 您应该永远这样做 (docs)。在堆上创建它并发送它的指针。

    在这种情况下,您可能希望通过 QML 控制它的生命周期,您可以通过调用 QQmlEngine::setObjectOwnership( s, QQmlEngine::JavaScriptOwnership) 来设置它。

    【讨论】:

    • 那页是圣杯:多么完美的综合。 :)
    • 谢谢,这行得通,但是我不明白 Qml 垃圾收集器是如何工作的,因为现在我在堆中创建元素,将对象所有权设置为 JavaScriptOwnership 并使用该对象发出信号。在 Qml 我只有日志函数调用,所以没有更多指向该对象的指针,但是我没有看到我的对象被删除(不调用析构函数)。稍后会在内存已满时调用它(如在 Java 中),还是在没有人指向对象后立即删除对象(在这种情况下我再次遇到错误)?
    • 来自文档 (doc.qt.io/qt-5/…):“引擎将定期收集 JavaScript 堆上所有未引用的数据”。这里的关键词是“周期性”,你无法保证它什么时候会发生(除非你手动调用收集器)。
    • 我不明白为什么你认为在这种情况下如果“Qml delete object after no one point on the object”会出错,因为对象是信号的参数,所以有至少是整个信号处理程序块的引用。
    【解决方案2】:

    QObject 不应在信号和槽中按值使用。此外,当QObject 本身隐藏其复制构造函数时,为 QObject 子类实现复制构造函数是一个坏主意。

    因此,更改您的信号以将指针传递给 QObject 就可以了。有一个short and good reference on how to communicate between C++ and Qml

    ps:您不需要注册声明Q_OBJECT 宏的类。

    【讨论】:

    • 对复制构造函数的好评论,那是非常糟糕的做法。
    【解决方案3】:

    这是我为自己制作的一个用于测试 C++ 的示例 QML 交互:

    //File: animal.h
    #ifndef ANIMAL_H
    #define ANIMAL_H
    
    #include <QObject>
    
    class Animal : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString animal_name READ get_animal_name WRITE set_animal_name)
    public:
        explicit Animal(QObject *parent = 0);
        QString get_animal_name();
        void set_animal_name(QString p_name);
    private:
        QString     animal_name;
    
    };
    
    #endif // ANIMAL_H
    
    //File: animal.cpp
    #include "animal.h"
    
    Animal::Animal(QObject *parent) : QObject(parent)
    {
    }
    void Animal::set_animal_name(QString p_name) {
        animal_name=p_name;
    }
    QString Animal::get_animal_name() {
        return animal_name;
    }
    
    //File: zoo.h
    #ifndef ZOO_H
    #define ZOO_H
    
    #include <QObject>
    
    class Animal;
    class Zoo : public QObject
    {
        Q_OBJECT
    public:
        explicit Zoo(QObject *parent = 0);
        Q_INVOKABLE Animal* get_animal_by_index(int index);
        Q_INVOKABLE void add_animal(Animal *a);
        Q_INVOKABLE void dump_animal_info(Animal *a);
        Q_INVOKABLE void emit_signal();
    signals:
        void some_signal(Animal *animal_object);
    public slots:
    
    private:
           QList<Animal*>       animals;
    };
    
    #endif // ZOO_H
    
    
    //File: zoo.cpp
    #include <QDebug>
    #include "zoo.h"
    #include "animal.h"
    
    Zoo::Zoo(QObject *parent) : QObject(parent)
    {
        Animal *a;
    
        a=new Animal();
        a->set_animal_name("Black Bear");
        add_animal(a);
    
        a=new Animal();
        a->set_animal_name("Gray Wolf");
        add_animal(a);
    }
    Animal* Zoo::get_animal_by_index(int index) {
        return animals.at(index);
    }
    void Zoo::add_animal(Animal *a) {
        animals.append(a);
    }
    void Zoo::dump_animal_info(Animal *a) {
        qWarning() << "animal_name=" << a->get_animal_name();
    }
    void Zoo::emit_signal() {
        Animal *a;
        a=animals.at(0);
        emit some_signal(a);
    }
    
    //File: main.cpp
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    
    #include "animal.h"
    #include "zoo.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<Zoo>("zoo",1,0,"Zoo");
        qmlRegisterType<Animal>("zoo.animal",1,0,"Animal");
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
        return app.exec();
    }
    
    //File: main.qml
    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    
    import zoo 1.0
    import zoo.animal 1.0
    
    ApplicationWindow {
        visible: true
        width: 640; height: 480; title: qsTr("Zoo")
    
        Zoo {
            id: zoopark
        }
        Column {
            Button {
                text: "Test C++ <--> QML data exchange by Method"
                onClicked: {
                    var animal=zoopark.get_animal_by_index(1);
                    zoopark.dump_animal_info(animal);
                }
            }
            Button {
                text: "Text C++ <--> QML data exchage by Signal"
                onClicked: {
                    zoopark.emit_signal();
                }
            }
        }
        Connections {
            target: zoopark
            onSome_signal: {
                console.log('signal received');
                console.log('from qml: animal name=' + animal_object.animal_name)
                console.log('dumping animal info from c++:')
                zoopark.dump_animal_info(animal_object)
            }
        }
    
        function process_signal() {
            console.log('from qml animal name=' + animal_object.animal_name)
            zoopark.dump_animal_info(animal_object)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-16
      • 1970-01-01
      相关资源
      最近更新 更多