Extending QML - Inheritance and Coercion Example

This example builds on the Extending QML - Adding Types Example and the Extending QML - Object and List Property Types Example .

The Inheritance and Coercion Example shows how to use base classes to assign types of more than one type to a property. It specializes the Person type developed in the previous examples into two types - a Boy and a Girl.

Declare Boy and Girl

The Person class remains unaltered in this example and the Boy and Girl C++ classes are trivial extensions of it. The types and their QML name are registered with the QML engine.

As an example, the inheritance used here is a little contrived, but in real applications it is likely that the two extensions would add additional properties or modify the Person classes behavior.

Running the Example

The BirthdayParty type has not changed since the previous example. The celebrant and guests property still use the People type.

However, as all three types, Person, Boy and Girl, have been registered with the QML system, on assignment QML automatically (and type-safely) converts the Boy and Girl objects into a Person.

The main.py file in the example includes a simple shell application that loads and runs the QML snippet shown below.

"""PySide6 port of the qml/examples/qml/referenceexamples/coercion example from Qt v6.x"""

from pathlib import Path
import sys

from PySide6.QtCore import QCoreApplication, QUrl
from PySide6.QtQml import QQmlComponent, QQmlEngine

from person import Boy, Girl
from birthdayparty import BirthdayParty


app = QCoreApplication(sys.argv)
qml_file = Path(__file__).parent / "example.qml"
url = QUrl.fromLocalFile(qml_file)
engine = QQmlEngine()
component = QQmlComponent(engine, url)
party = component.create()
if not party:
    print(component.errors())
    del engine
    sys.exit(-1)
host = party.host
print(f"{host.name} is having a birthday!")
if isinstance(host, Boy):
    print("He is inviting:")
else:
    print("She is inviting:")
for g in range(party.guestCount()):
    name = party.guest(g).name
    print(f"    {name}")
del engine
sys.exit(0)
from PySide6.QtCore import QObject, Property
from PySide6.QtQml import QmlElement, ListProperty

from person import Person


# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
QML_IMPORT_NAME = "examples.coercion.people"
QML_IMPORT_MAJOR_VERSION = 1


@QmlElement
class BirthdayParty(QObject):

    def __init__(self, parent=None):
        super().__init__(parent)
        self._host = None
        self._guests = []

    @Property(Person)
    def host(self):
        return self._host

    @host.setter
    def host(self, h):
        self._host = h

    def guest(self, n):
        return self._guests[n]

    def guestCount(self):
        return len(self._guests)

    def appendGuest(self, guest):
        self._guests.append(guest)

    guests = ListProperty(Person, appendGuest)
from PySide6.QtCore import QObject, Property
from PySide6.QtQml import QmlElement, QmlUncreatable

# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
QML_IMPORT_NAME = "examples.coercion.people"
QML_IMPORT_MAJOR_VERSION = 1


@QmlElement
@QmlUncreatable("Person is an abstract base class.")
class Person(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._name = ''
        self._shoe_size = 0

    @Property(str)
    def name(self):
        return self._name

    @name.setter
    def name(self, n):
        self._name = n

    @Property(int)
    def shoe_size(self):
        return self._shoe_size

    @shoe_size.setter
    def shoe_size(self, s):
        self._shoe_size = s


@QmlElement
class Boy(Person):
    def __init__(self, parent=None):
        super().__init__(parent)


@QmlElement
class Girl(Person):
    def __init__(self, parent=None):
        super().__init__(parent)
import examples.coercion.people

BirthdayParty {
    host: Boy {
        name: "Bob Jones"
        shoe_size: 12
    }
    guests: [
        Boy { name: "Leo Hodges" },
        Boy { name: "Jack Smith" },
        Girl { name: "Anne Brown" }
    ]
}