My goal is to pass an instance of the custom C++ class “Contact” as a parameter “contact” of a signal “contactFound” emitted from the C++ class “ContactHelper” to QML; and in QML in the “onContactFound” signal handler to access the properties of the signal parameter “contact”.
From the QML side, the object “contact” is basically a data structure, with 2 read-only properties, and no methods.
I have tried 2 approaches, both shown in the example code at the bottom of this question. EDIT: In the original version of this question, neither worked. Thanks to comments from @BaCaRoZzo, approach 1 now works (and thus solves my immediate problem).
Approach 1) “Contact” is modelled on the Custom Type examples “Block” and “Message” in the QT documentation. i.e. “Contact” is declared via qRegisterMetaType, and is NOT based on QObject, but uses the Q_GADGET macro (following input from BaCaRoZzo).
This approach works both when contact is passed to the signal by reference (&contact), and by value (contact), but fails when passed by by pointer / address (*contact).
Approach 2) “Contact2” based on a QObject, declared via qmlRegisterType, and passed by pointer / address (*contact2).
The code compiles and runs, but neither approach gave me the expected signal parameter property access.
Approach 1) in QML the parameter “contact” of “onContactFound” is valid, but the properties of contact are undefined. EDIT: this is fixed by adding the missing Q_GADGET macro to contact.h
Approach 2) in QML the parameter “contact” of “onContactFound2” is not defined!
EDIT: Given that Approach 1 now works, I can use it in my real code, and slightly change the focus of this question, namely:
a) What is stopping Approach 2 (Object / qmlRegisterType / passed by pointer *) from working?
b) Assuming both approaches can be got working, which is the most suitable for transferring a light-weight read-only data-structure (a handful of string properties + 1 string array property) via signal parameter from C++ to QML?
I am developing with Qt5.5.1, with an Android 6.0.1 device as the target.
The example code below can be pasted into a new “QtQuick Controls Application” project.
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "contactshelper.h"
#include "contact.h"
#include "contact2.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
qmlRegisterType<ContactsHelper>("ContactsHelper",1,0,"ContactsHelper");
qRegisterMetaType<Contact>();
qmlRegisterType<Contact2>("ContactsHelper",1,0,"Contact");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
contact.h
#ifndef CONTACT_H
#define CONTACT_H
#include <QDebug>
#include <QMetaType>
class Contact {
Q_GADGET //!!!This macro was missing in the original question!!!
public:
Contact();
Contact(const Contact &other);
~Contact();
Q_PROPERTY(QString contactId MEMBER m_contactId)
Q_PROPERTY(QString displayLabel MEMBER m_displayLabel)
void setContactId(const QString contactId);
void setDisplayLabel(const QString displayLabel);
private:
QString m_contactId;
QString m_displayLabel;
};
Q_DECLARE_METATYPE(Contact)
#endif // CONTACT_H
contact.cpp
#include "contact.h"
#include <QDebug>
Contact::Contact(){}
Contact::~Contact(){}
Contact::Contact(const Contact &other) {
qDebug() << "Contact: copy constructor called: " << other.m_displayLabel;
m_contactId = other.m_contactId;
m_displayLabel = other.m_displayLabel;
}
void Contact::setContactId(const QString contactId) {
m_contactId = contactId;
}
void Contact::setDisplayLabel(const QString displayLabel) {
m_displayLabel = displayLabel;
}
contact2.h
#ifndef CONTACT2_H
#define CONTACT2_H
#include <QDebug>
#include <QObject>
class Contact2: public QObject
{
Q_OBJECT
public:
explicit Contact2(QObject *parent = 0);
~Contact2();
Q_PROPERTY(QString contactId MEMBER m_contactId)
Q_PROPERTY(QString displayLabel MEMBER m_displayLabel)
void setContactId(const QString contactId);
void setDisplayLabel(const QString displayLabel);
private:
QString m_contactId;
QString m_displayLabel;
};
#endif // CONTACT2_H
contact2.cpp
#include "contact2.h"
Contact2::Contact2(QObject *parent) : QObject(parent) {}
Contact2::~Contact2() {}
void Contact2::setContactId(const QString contactId) {
m_contactId = contactId;
}
void Contact2::setDisplayLabel(const QString displayLabel) {
m_displayLabel = displayLabel;
}
contactshelper.h
#ifndef CONTACTSHELPER_H
#define CONTACTSHELPER_H
#include "contact.h"
#include "contact2.h"
#include <QObject>
class ContactsHelper : public QObject {
Q_OBJECT
public:
explicit ContactsHelper(QObject *parent = 0);
Q_INVOKABLE void fakeContactFound();
Q_INVOKABLE void fakeContactFound2();
signals:
void contactFound(const int index, const int count, const Contact &contact);
void contactFound2(const int index, const int count, const Contact *contact);
};
#endif // CONTACTSHELPER_H
contactshelper.cpp
#include "contactshelper.h"
#include "contact.h"
ContactsHelper::ContactsHelper(QObject *parent) : QObject(parent) {}
void ContactsHelper::fakeContactFound() {
qDebug() << "ContactsHelper: faking contactFound";
Contact contact;
contact.setContactId("123");
contact.setDisplayLabel("Aunt Agatha");
contactFound(1, 1, contact);
}
void ContactsHelper::fakeContactFound2() {
qDebug() << "ContactsHelper: faking contactFound";
Contact2 *contact = new Contact2;
contact->setContactId("123");
contact->setDisplayLabel("Uncle Fred");
this->contactFound2(1, 1, contact);
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
import ContactsHelper 1.0
ApplicationWindow {
visible: true
ContactsHelper {
id: contactsHelper
onContactFound: {
console.log ("QML: onContactFound signal received!");
console.log ("QML: displayLabel: " + contact.displayLabel);
listProperty(contact);
}
onContactFound2: {
console.log ("QML: onContactFound2 signal received!");
console.log ("QML: displayLabel: " + contact.displayLabel);
listProperty(contact);
}
function listProperty(item) {
console.log ("QML: listing contact properties ...");
for (var p in item)
console.log(p + ": " + item[p]);
}
}
MainForm {
anchors.fill: parent
button1.onClicked: {contactsHelper.fakeContactFound()}
button2.onClicked: {contactsHelper.fakeContactFound2()}
}
}
MainForm.ui.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item {
width: 640
height: 480
property alias button1: button1
property alias button2: button2
RowLayout {
anchors.centerIn: parent
Button {
id: button1
text: qsTr("Fake contactFound")
}
Button {
id: button2
text: qsTr("Fake contactFound2")
}
}
}