将自定义 C++ 对象传递给 Qml 错误(无属性)
Pass custom C++ object to Qml error (no properties)
我正在尝试编写代码,通过信号将一些数据从 C++ 引擎传递到 Qml 脚本,但看起来我做错了什么,因为当我在 Qml 中接收到信号时,我的对象没有任何方法或属性!看看那个代码:
Signaller - class 调用信号:
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 将传递给 Qml 引擎,并且必须在 Qml 脚本中具有测试方法:
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";
}
这2个classes也在main函数中注册:
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后为空。
我做错了什么?
您正在通过 signal/slot 系统按值传递 QObject
派生对象 - 您应该永远不要 这样做 (docs)。在堆上创建它并发送它的指针。
在这种情况下,您可能希望通过 QML 控制它的生命周期,您可以通过调用 QQmlEngine::setObjectOwnership( s, QQmlEngine::JavaScriptOwnership)
.
来设置它
QObject
不应在信号和槽中按值使用。此外,当 QObject
本身隐藏其复制构造函数时,为 QObject 子类实现复制构造函数是一个坏主意。
因此,将您的信号更改为将指针传递给 QObject 就可以了。有个short and good reference on how to communicate between C++ and Qml
ps: 你不需要注册 类 来声明 Q_OBJECT
宏。
这是我为自己制作的用于测试 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)
}
}
我正在尝试编写代码,通过信号将一些数据从 C++ 引擎传递到 Qml 脚本,但看起来我做错了什么,因为当我在 Qml 中接收到信号时,我的对象没有任何方法或属性!看看那个代码:
Signaller - class 调用信号: 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 将传递给 Qml 引擎,并且必须在 Qml 脚本中具有测试方法:
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";
}
这2个classes也在main函数中注册:
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后为空。 我做错了什么?
您正在通过 signal/slot 系统按值传递 QObject
派生对象 - 您应该永远不要 这样做 (docs)。在堆上创建它并发送它的指针。
在这种情况下,您可能希望通过 QML 控制它的生命周期,您可以通过调用 QQmlEngine::setObjectOwnership( s, QQmlEngine::JavaScriptOwnership)
.
QObject
不应在信号和槽中按值使用。此外,当 QObject
本身隐藏其复制构造函数时,为 QObject 子类实现复制构造函数是一个坏主意。
因此,将您的信号更改为将指针传递给 QObject 就可以了。有个short and good reference on how to communicate between C++ and Qml
ps: 你不需要注册 类 来声明 Q_OBJECT
宏。
这是我为自己制作的用于测试 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)
}
}