> ## Content Index
> Fetch the complete content index at: https://www.smallstepsystems.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Practical QML: Exposing QObject Subclasses to QML
- URL: https://www.smallstepsystems.com/practical-qml-exposing-qobject-subclasses-to-qml/
- Published: 2013-08-10T16:19:50.000Z
- Updated: 2026-01-25T16:34:55.000Z
- Author: Burkhard Stubert

How can we pass an instance of a *QObject* subclass from a Qt/C++ list model to a QML list view? How can we expose a property from a Qt/C++ class to QML, where the type of the property is derived from QObject?

We want to develop a contact list in QML, where the contact data comes from a Qt/C++ model. The finished contact list looks as following:  

![contact_list](https://storage.ghost.io/c/c7/ee/c7ee1cf5-b86f-491b-aab7-20ad081598e1/content/images/2025/11/contact_list.png)

  
The following code shows how the list view is implemented in QML:ListView { anchors { fill: parent; margins: 8 } delegate: Column { height: 56 Text { text: contact.firstName + " " + contact.lastName color: "blue" font.pixelSize: 20 } Text { text: contact.address.zipCode + " " + contact.address.city color: "blue" font.pixelSize: 16 } } model: ContactModel {} }

The QML *ListView* adheres to Qt's normal model-view-delegate pattern. It gets all its data from the model, *ContactModel*, which is implemented in Qt/C++ in our case. The model provides *contact* as its single role. The delegate presents each contact in two lines. The first line shows the first and last name. The second line shows the zip code and the city.  
The delegate uses the *contact* object provided by the model and reads the values of the *contact*'s properties *firstName*, *lastName* and *address*. The property *address* is itself an object with its own properties *zipCode* and *city*. In QML lingo, *address* is an attached property of *contact*.  
When the QML engine instantiates the first item of the contact list from the delegate component (which is basically a factory class for creating contact items), it conceptually creates the following QML item.

Column { height: 56 Text { ... } // name as before Text { ... } // address as before Contact { id: contact firstName: "Susanne"; lastName: "Meier" Address { id: address zipCode: "86332"; city: "Ettal" } } }

The other items of the contact list are handled similarly.  
Now that we know what we want in QML, we must figure out how to implement our wishes in Qt/C++. Let us work our way top-down, from the *ContactModel* class over the *Contact* class to the *Address* class.  
Whenever we want to instantiate a C++ class like ContactModel in QML, we must register the class as a QML type. We add the line

qmlRegisterType<ContactModel>("com.embeddeduse.models", 1, 0, "ContactModel");

to the *main()* function of our program. The function *qmlRegisterType()* is declared in the header *QtQml/qqml.h*. It tells the QML engine that the C++ class *ContactModel* is used under the name of *ContactModel* (the last argument of the function) in QML and that this new QML component is found in version 1.0 of the library "com.embeddeduse.models". In order to use the C++ class *ContactModel* like any other built-in QML component in our QML code, we have to add the statement

import com.embeddeduse.models 1.0

to our QML file.  
The class *ContactModel* derives from *QAbstractListModel*. The contact model stores its contacts in the member variable *m\_contacts* of type *QList<Contact \*>*. We must implement the three pure virtual functions *data()*, *parent()* and *rowCount()* and the virtual function *roleNames()*.

QVariant ContactModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= rowCount() || !index.isValid()) return QVariant(); if (role == ROLE\_CONTACT) { QVariant v; v.setValue(static\_cast<QObject \*>(m\_contacts\[index.row()\])); return v; } return QVariant(); } QModelIndex ContactModel::parent(const QModelIndex &child) const { Q\_UNUSED(child); return QModelIndex(); } int ContactModel::rowCount(const QModelIndex &parent) const { Q\_UNUSED(parent); return m\_contacts.count(); } QHash ContactModel::roleNames() const { static QHash roles; if (roles.isEmpty()) roles\[ROLE\_CONTACT\] = "contact"; return roles; }

The functions *parent()* and *rowCount()* are straightforward and need no further explanation. Our first shot at implementing the *data()* function might look "simpler" than the solution above.

if (role == ROLE\_CONTACT) { return m\_contacts\[index.row()\]; }

This does not even compile, because neither a constructor *QVariant(Contact \*)* nor a constructor *QVariant(QObject \*)* exist. The *QVariant* constructor is called because *data()* returns a *QVariant*.  
The compile error is nothing but a reminder that we must make our *Contact* class known to Qt's meta-object system. Otherwise, *QVariant* cannot handle it. Our first reflex may be to use *qRegisterMetaType()*. However, it would require our class to have a copy constructor, which would require *QObject* to have a copy constructor - which it doesn't have (the documentation of the *Q\_DISABLE\_COPY* macro gives the reason).  
This is normally the point where people give up and come up with not-so-nice workarounds. But not us! Fortunately, *QVariant* provides an alternative way to "register" subclasses of *QObject*. The template function *void setValue(const T& value)* allows us to store values of type *T* in a *QMetaType* object and hence in a *QVariant* object, if type *T* is not supported by *QVariant*. Its counterpart *T value() const* allows us to read the stored value again. *QMetaType* contains special code to handle *QObjects*. As long as we only want to use properties, signals, slots and invokable functions of our custom class derived from QObject, we are fine. With the help of *setValue()*, we can wrap our *Contact* pointer - cast to a pointer of its base class *QObject* \- in a *QVariant* and pass it to QML. This finally explains these five lines from *ContactModel::data()* of how to pass a Contact object to the QML view.

if (role == ROLE\_CONTACT) { QVariant v; v.setValue(static\_cast<QObject \*>(m\_contacts\[index.row()\])); return v; }

The function *ContactModel::roleNames()* is much easier to explain. It associates the C++ roles (given as enum constants) with the QML property names. In Qt 5, we must reimplement the virtual function *roleNames()* to set this association. In Qt 4, we could use the function *setRoleNames()*, which is not available in Qt 5 any more.  
The most difficult part of our little project is done. Let us now look at the code of the *Contact* class. The relevant part of its declaration looks as follows:

class Contact : public QObject { Q\_OBJECT Q\_DISABLE\_COPY(Contact) Q\_PROPERTY(QString firstName READ firstName WRITE setFirstName NOTIFY firstNameChanged) Q\_PROPERTY(QString lastName READ lastName WRITE setLastName NOTIFY lastNameChanged) Q\_PROPERTY(QString phoneNumber READ phoneNumber WRITE setPhoneNumber NOTIFY phoneNumberChanged) Q\_PROPERTY(Address \*address READ address WRITE setAddress NOTIFY addressChanged) // Implementation of the getters, setters and signals of the properties ...

The class *Address* is implemented very similar to *Contact* \- except that it doesn't have a property like *address* of a type derived from *QObject*. It simply defines the *QString* properties *street*, *zipCode* and *city* together with all the necessary boiler-plate getters, setters and notification signals.  
The code of *Contact* as given above compiles without any complaints. However, it doesn't work properly. The contact items lack the line with the address. Moreover, the QML engine emits the error message

QMetaProperty::read: Unable to handle unregistered datatype 'Address\*' for property 'Contact::address'

because it doesn't know anything about our *Address* class. From our discussion about implementing *ContactModel::data()*, we know that we cannot register *Address* with *QVariant*. Similar to our solution for *data()*, we could camouflage the *address* property as a *QObject* pointer. However, this would require some ugly casting in the property's setter.  
Fortunately, there is a pretty nice solution and we know it already. We simply register *Address* as a QML component in *main()*:

qmlRegisterType<Address>("com.embeddeduse.models";, 1, 0, "Address");

No runtime errors any more. We have exposed a property from C++ to QML, where the type of the property is a subclass of *QObject*. Of course, we cannot only read data from such a property, but we can also write data to such a property. We demonstrate this by adding a *MouseArea* to the *Text*, which presents the zip code and city.

Text { text: contact.address.zipCode + " " + contact.address.city color: "blue" font.pixelSize: 16 MouseArea { anchors.fill: parent onReleased: contact.address.city = "Heidelberg" } }

Whenever the user clicks on the line with the zip code and city name, the city name changes to "Heidelberg". The city name is instantly changed in the list view, because the setter function for the *address*'s *city* property emits a signal notifying its observers about the changed city name.  
So in the end, everyone lives happily in Heidelberg - not a bad result at all.