Search code examples
c++jsonqtcommunicationqmetatype

How to register a class for use it in a QWebChannel signal in Qt


Im using QT 5 with a WebChannel to communicate with a HTML Page. I successfully able to communicate texts and strings. however i woud like to communicate some points data.

the official documentation says

"No manual message passing and serialization of data is required," http://doc.qt.io/qt-5/qwebchannel.html

When i try to send a object or an array, instead of a string, the application throw this message:

Don't know how to handle 'data', use qRegisterMetaType to register it.

How can I emit a signal with a Point class instead of a string object? Or to serialize a class to a string.

This is the object that i would like to send to my HTML

Point class

class MyData{
public :
    int x,y,z;
};
Q_DECLARE_METATYPE(MyData)

Map class

class MyMap : public QObject{
Q_OBJECT

public:
    explicit MyMap ();

signals:
    updateText(const QString &text); // registered
    updateData(MyData &data); //   Don't know how to handle 'data', use qRegisterMetaType to register it.

public slots:
    receiveText(const QString &text);
};

and the main code

qRegisterMetaType<MyData>();

// setup the channel
QWebChannel channel;
QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected, &channel, &QWebChannel::connectTo);

// setup the dialog and publish it to the QWebChannel
channel.registerObject(QStringLiteral("map"), &map);

map.updateText("text");

MyData data;
data.x=10;
data.y=12;
data.z=13;
map.updateData(data);

Solution

  • Per Qt documentation:

    To use the type T in QVariant, using Q_DECLARE_METATYPE() is sufficient. To use the type T in queued signal and slot connections, qRegisterMetaType() must be called before the first connection is established.

    In my own code, I use Q_DECLARE_METATYPE for classes I want to store in a QVariant (this macro has to by put at global scope). And I qRegisterMetaType<T>() for classes I want to be passed to signals/slots (this function has to be called before the signal.slot connection is made). I never had to call both if using only one feature.

    So, for your example, this should work:

    qRegisterMetaType<MyData>( "MyData" );
    // then, form here, you can connect `updateData(MyData)` signal to any slot taking a MyData parameter
    

    For signal/slot declaration:

    • If qRegisterMetaType was not called, you can only use signal/slot passing MyData by reference or pointer (declare the signal as void updateData(MyData& data); or void updateData(MyData* data);). Then, you must be very careful if doing asynchonous connection (like Qt::QueuedConnection) because, as the slot will be executed "later", you must guarantee that object reference remains valid untill the slot gets executed after you emited the signal.
    • If qRegisterMetaType was called, you can use signal/slot passing MyData by copy (declare the signal as void updateData(MyData data);). And it's safe to do asynchonous connection with those objects passed by copy as Qt is able to serialize the object (meaning that if the slot is executed "later" it will in the end be called with a brand new MyData object copied from the original one you emited the signal with....without qRegisterMetaType being called, Qt is unable to do this "copy").

    Make sure MyData declaration is known when you do this (no forward declaration).

    Note also that not all classes/structs can be registered this way, they must have:

    a public default constructor, a public copy constructor, and a public destructor

    It's the case for your MyData class (default ones are OK), so it should work smoothly.