Lobster
Lobster

Reputation: 635

Pass custom C++ object to Qml error (no properties)

I am trying to write code that will pass some data from C++ engine to the Qml scripts via signal, but it looks like that I doing some thing wrong, because when I receive signal in Qml my object don't any method or properties! Look at that code:

Signaller - class that invoke signal: 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 - class that will be passed to Qml engine, and which must have test method in Qml script:

Test.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";
}

Those 2 classes also registered in main function:

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);
}

That is my Qml file with test code:

import Signaller 1.0

Page {

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

And the log:

[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() 

As you can see from log, TestClass.test field is empty after passing to the Qml. What I am doing wrong?

Upvotes: 1

Views: 2616

Answers (3)

Nulik
Nulik

Reputation: 7360

This is an example I made for myself for testing C++ <--> QML interaction:

//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)
    }
}

Upvotes: 3

UmNyobe
UmNyobe

Reputation: 22890

QObject should not be used by value in signals and slots. Also, It is a bad idea to implement a copy constructor for a QObject subclass when QObject itself hides its copy constructor.

Thus change your signals to pass pointers to QObject and It will be fine. There is a short and good reference on how to communicate between C++ and Qml

ps: You don't need to register classes which declare the Q_OBJECT macro.

Upvotes: 3

cmannett85
cmannett85

Reputation: 22346

You are passing a QObject derived object by value through the signal/slot system - you should never do this (docs). Create it on the heap and send it's pointer through.

In this case you'll probably want it's lifetime controlled via QML, you can set that by calling QQmlEngine::setObjectOwnership( s, QQmlEngine::JavaScriptOwnership).

Upvotes: 6

Related Questions