Extending QML (advanced) - Default Properties#
This is the third of a series of 6 examples forming a tutorial using the example of a birthday party to demonstrate some of the advanced features of QML.
Currently, in the QML file, each property is assigned explicitly. For example,
the host
property is assigned a Boy
and the guests
property is
assigned a list of Boy
or Girl
. This is easy but it can be made a bit
simpler for this specific use case. Instead of assigning the guests
property explicitly, we can add Boy
and Girl
objects inside the party
directly and have them assigned to guests
implicitly. It makes sense that
all the attendees that we specify, and that are not the host, are guests. This
change is purely syntactical but it can add a more natural feel in many
situations.
The guests
property can be designated as the default property of
BirthdayParty
. Meaning that each object created inside of a
BirthdayParty
is implicitly appended to the default property guests
.
The resulting QML looks like this.
6BirthdayParty {
7 host: Boy {
8 name: "Bob Jones"
9 shoe_size: 12
10 }
11
12 Boy { name: "Leo Hodges" }
13 Boy { name: "Jack Smith" }
14 Girl { name: "Anne Brown" }
15}
The only change required to enable this behavior is to add the DefaultProperty
class info annotation to BirthdayParty
to designate guests
as its default
property.
16@QmlElement
17@ClassInfo(DefaultProperty="guests")
18class BirthdayParty(QObject):
You may already be familiar with this mechanism. The default property for all
descendants of Item
in QML is the data
property. All elements not
explicitly added to a property of an Item
will be added to data
. This
makes the structure clear and reduces unnecessary noise in the code.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example from Qt v6.x"""
from pathlib import Path
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
from person import Boy, Girl
from birthdayparty import BirthdayParty
app = QCoreApplication(sys.argv)
engine = QQmlEngine()
engine.addImportPath(Path(__file__).parent)
component = QQmlComponent(engine)
component.loadFromModule("People", "Main")
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)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtCore import QObject, ClassInfo, Property, Signal
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 = "People"
QML_IMPORT_MAJOR_VERSION = 1
@QmlElement
@ClassInfo(DefaultProperty="guests")
class BirthdayParty(QObject):
host_changed = Signal()
guests_changed = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self._host = None
self._guests = []
@Property(Person, notify=host_changed, final=True)
def host(self):
return self._host
@host.setter
def host(self, h):
if self._host != h:
self._host = h
self.host_changed.emit()
def guest(self, n):
return self._guests[n]
def guestCount(self):
return len(self._guests)
def appendGuest(self, guest):
self._guests.append(guest)
self.guests_changed.emit()
guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlAnonymous, QmlElement
# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
QML_IMPORT_NAME = "People"
QML_IMPORT_MAJOR_VERSION = 1
@QmlAnonymous
class Person(QObject):
name_changed = Signal()
shoe_size_changed = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self._name = ''
self._shoe_size = 0
@Property(str, notify=name_changed, final=True)
def name(self):
return self._name
@name.setter
def name(self, n):
if self._name != n:
self._name = n
self.name_changed.emit()
@Property(int, notify=shoe_size_changed, final=True)
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)
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import People
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoe_size: 12
}
Boy { name: "Leo Hodges" }
Boy { name: "Jack Smith" }
Girl { name: "Anne Brown" }
}
module People
typeinfo coercion.qmltypes
Main 1.0 Main.qml