C++를 사용한 고급 QML 확장 기능 작성
BirthdayParty 기본 프로젝트
extending-qml-advanced/advanced1-Base-project
이 튜토리얼에서는 생일 파티를 예로 들어 QML의 몇 가지 기능을 설명합니다. 아래에서 설명하는 다양한 기능에 대한 코드는 이 생일 파티 프로젝트를 기반으로 하며, QML 확장 기능에 대한 첫 번째 튜토리얼의 일부 내용을 활용합니다. 이 간단한 예제를 바탕으로 아래에서 설명하는 다양한 QML 확장 기능을 예시하기 위해 코드를 확장해 나갑니다. 코드에 추가되는 각 새로운 확장 기능에 대한 전체 코드는 각 섹션 제목 아래에 명시된 위치의 튜토리얼에서 확인하거나, 이 페이지 맨 하단에 있는 코드 링크를 따라가면 볼 수 있습니다.
기본 프로젝트에는 참석자와 파티 자체를 각각 모델링하는 ` Person ` 클래스와 ` BirthdayParty ` 클래스가 정의되어 있습니다.
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
QML_ELEMENT
...
QString m_name;
int m_shoeSize = 0;
};
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
QML_ELEMENT
...
Person *m_host = nullptr;
QList<Person *> m_guests;
};그런 다음 파티에 대한 모든 정보를 해당 QML 파일에 저장할 수 있습니다.
BirthdayParty {
host: Person {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Person { name: "Leo Hodges" },
Person { name: "Jack Smith" },
Person { name: "Anne Brown" }
]
}main.cpp 파일은 오늘이 누구의 생일인지, 그리고 그 파티에 누가 초대되었는지를 표시하는 간단한 셸 애플리케이션을 생성합니다.
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadFromModule("People", "Main");
std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };이 앱은 파티에 대한 다음과 같은 요약 정보를 출력합니다.
"Bob Jones" is having a birthday!
They are inviting:
"Leo Hodges"
"Jack Smith"
"Anne Brown"다음 섹션에서는 상속과 형 변환을 사용하여 Person 대신 Boy 및 Girl 참석자를 지원하는 방법, 기본 속성을 활용하여 파티 참석자를 암시적으로 게스트로 지정하는 방법, 속성을 하나씩 지정하는 대신 그룹으로 지정하는 방법, 부착된 객체를 사용하여 초대받은 손님의 응답을 추적하는 방법, 속성 값 소스를 사용하여 시간이 지남에 따라 ‘생일 축하합니다’ 노래 가사를 표시하는 방법, 그리고 타사 객체를 QML에 노출하는 방법에 대해 다룹니다.
상속과 형변환
extending-qml-advanced/advanced2-Inheritance-and-coercion
현재 각 참석자는 ‘사람’으로 모델링되어 있습니다. 이는 다소 너무 일반적인 개념이므로, 참석자에 대해 더 자세한 정보를 알 수 있다면 좋을 것입니다. 참석자를 ‘남자’와 ‘여자’로 세분화하면 누가 오는지 더 명확히 파악할 수 있습니다.
이를 위해 ` Boy ` 및 ` Girl ` 클래스가 도입되었으며, 두 클래스 모두 ` Person`을 상속받습니다.
class Boy : public Person
{
Q_OBJECT
QML_ELEMENT
public:
using Person::Person;
};
class Girl : public Person
{
Q_OBJECT
QML_ELEMENT
public:
using Person::Person;
};Person 클래스는 변경되지 않았으며, Boy 및 Girl C++ 클래스는 이를 기반으로 한 간단한 확장입니다. 유형과 해당 QML 이름은 QML_ELEMENT 을 통해 QML 엔진에 등록됩니다.
BirthdayParty 의 host 및 guests 속성은 여전히 Person 의 인스턴스를 받아준다는 점에 유의하십시오.
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
QML_ELEMENT
...
};Person 클래스 자체의 구현은 변경되지 않았습니다. 그러나 Person 클래스가 Boy 및 Girl 의 공통 기반 클래스로 용도가 변경됨에 따라, Person 는 더 이상 QML에서 직접 인스턴스화할 수 없습니다. 대신 명시적인 Boy 또는 Girl 를 인스턴스화해야 합니다.
class Person : public QObject
{
...
QML_ELEMENT
QML_UNCREATABLE("Person is an abstract base class.")
...
};QML 내부에서 Person 의 인스턴스 생성을 허용하지 않으려는 반면, 이 유형을 속성 유형으로 사용하거나 다른 유형을 이 유형으로 강제 변환할 수 있도록 QML 엔진에 등록해야 합니다. 이것이 바로 QML_UNCREATABLE 매크로가 수행하는 역할입니다. Person, Boy, Girl 이 세 가지 유형 모두 QML 시스템에 등록되었으므로, 할당 시 QML은 Boy 및 Girl 객체를 Person 로 자동(그리고 유형 안전하게) 변환합니다.
이러한 변경 사항이 적용됨에 따라, 이제 참석자에 대한 추가 정보를 포함하여 생일 파티를 다음과 같이 지정할 수 있습니다.
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Boy { name: "Leo Hodges" },
Boy { name: "Jack Smith" },
Girl { name: "Anne Brown" }
]
}기본 속성
extending-qml-advanced/advanced3-Default-properties
현재 QML 파일에서는 각 속성이 명시적으로 할당됩니다. 예를 들어, ` host ` 속성에는 ` Boy `가 할당되고, ` guests ` 속성에는 ` Boy ` 또는 ` Girl` 목록이 할당됩니다. 이 방법은 간편하지만, 이 특정 사용 사례의 경우 조금 더 단순화할 수 있습니다. guests 속성을 명시적으로 할당하는 대신, 파티 내부에 직접 Boy 및 Girl 객체를 추가하고 이를 guests 에 암시적으로 할당할 수 있습니다. 우리가 지정한 모든 참석자 중 호스트가 아닌 사람은 모두 게스트라는 점이 타당합니다. 이 변경 사항은 순전히 구문상의 것이지만, 많은 상황에서 더 자연스러운 느낌을 줄 수 있습니다.
guests 속성은 BirthdayParty 의 기본 속성으로 지정할 수 있습니다. 즉, BirthdayParty 내에서 생성된 각 객체는 암시적으로 기본 속성 guests 에 추가됩니다. 결과적인 QML은 다음과 같습니다.
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
Boy { name: "Leo Hodges" }
Boy { name: "Jack Smith" }
Girl { name: "Anne Brown" }
}이 동작을 활성화하기 위해 필요한 유일한 변경 사항은 BirthdayParty 에 DefaultProperty 클래스 정보 어노테이션을 추가하여 guests 을 기본 속성으로 지정하는 것입니다.
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
Q_CLASSINFO("DefaultProperty", "guests")
QML_ELEMENT
...
};이 메커니즘에 이미 익숙하실 수도 있습니다. QML에서 ` Item `의 모든 하위 요소에 대한 기본 속성은 ` data ` 속성입니다. ` Item `의 속성에 명시적으로 추가되지 않은 모든 요소는 ` data`에 추가됩니다. 이를 통해 구조가 명확해지고 코드의 불필요한 잡음을 줄일 수 있습니다.
그룹화된 속성
extending-qml-advanced/advanced4-Grouped-properties
손님들의 신발에 대한 더 많은 정보가 필요합니다. 사이즈 외에도 신발의 색상, 브랜드, 가격도 저장하고자 합니다. 이 정보는 ShoeDescription 클래스에 저장됩니다.
class ShoeDescription : public QObject
{
Q_OBJECT
Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
...
};이제 각 사람(person)은 ‘ name ’와 ‘shoe description shoe ’이라는 두 가지 속성을 갖게 됩니다.
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
...
};신발 설명xml-ph-0001@deepl.internal의 각 요소에 대한 값을 지정하는 것도 가능하지만, 다소 반복적인 작업입니다.
Girl {
name: "Anne Brown"
shoe.size: 7
shoe.color: "red"
shoe.brand: "Job Macobs"
shoe.price: 99.99
}그룹화된 속성(Grouped properties)을 사용하면 이러한 속성을 더 우아하게 할당할 수 있습니다. 각 속성에 값을 하나씩 할당하는 대신, 개별 값을 그룹으로 묶어 ` shoe ` 속성에 전달할 수 있어 코드의 가독성이 향상됩니다. 이 기능은 모든 QML에서 기본적으로 제공되므로 별도의 변경 사항 없이 바로 사용할 수 있습니다.
host: Boy {
name: "Bob Jones"
shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 }
}부착 속성
extending-qml-advanced/advanced5-Attached-properties
이제 주최자가 초대장을 발송할 차례입니다. 어떤 손님이 언제 초대장에 응답했는지 추적하려면 해당 정보를 저장할 장소가 필요합니다. 이 정보를 ` BirthdayParty ` 객체 자체에 저장하는 것은 적합하지 않습니다. 더 좋은 방법은 응답 정보를 파티 객체에 부속 객체로 저장하는 것입니다.
먼저, 게스트의 응답을 보관할 ` BirthdayPartyAttached ` 클래스를 선언합니다.
class BirthdayPartyAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL)
QML_ANONYMOUS
...
};그리고 이를 ` BirthdayParty ` 클래스에 연결하고, 연결된 객체를 반환하도록 ` qmlAttachedProperties() `을 정의합니다.
class BirthdayParty : public QObject
{
...
QML_ATTACHED(BirthdayPartyAttached)
...
static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
};이제 QML에서 첨부된 객체를 사용하여 초대받은 게스트의 RSVP 정보를 저장할 수 있습니다.
BirthdayParty {
Boy {
name: "Robert Campbell"
BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd")
}
Boy {
name: "Leo Hodges"
shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd")
}
host: Boy {
name: "Jack Smith"
shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
}
}마지막으로, 다음과 같은 방식으로 해당 정보에 접근할 수 있습니다.
QDate rsvpDate;
QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
if (attached)
rsvpDate = attached->property("rsvp").toDate();프로그램은 다가오는 파티에 대한 다음과 같은 요약 정보를 출력합니다.
"Jack Smith" is having a birthday!
He is inviting:
"Robert Campbell" RSVP date: "Wed Mar 1 2023"
"Leo Hodges" RSVP date: "Mon Mar 6 2023"속성 값 소스
extending-qml-advanced/advanced6-Property-value-source
파티가 진행되는 동안 손님들은 주최자를 위해 노래를 불러야 합니다. 손님들을 돕기 위해 프로그램에 이 행사에 맞춰 맞춤화된 가사를 표시할 수 있다면 유용할 것입니다. 이를 위해 속성 값 소스를 사용하여 시간에 따라 노래 가사를 생성합니다.
class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_INTERFACES(QQmlPropertyValueSource)
...
void setTarget(const QQmlProperty &) override;
};HappyBirthdaySong 클래스가 값 소스로 추가됩니다. 이 클래스는 QQmlPropertyValueSource 을 상속받아야 하며, Q_INTERFACES 매크로를 사용하여 QQmlPropertyValueSource 인터페이스를 구현해야 합니다. setTarget() 함수는 이 소스가 어떤 속성에 작용할지 정의하는 데 사용됩니다. 이 경우, 값 소스는 BirthdayParty 의 announcement 속성에 값을 기록하여 시간에 따라 가사를 표시합니다. 이 소스에는 내부 타이머가 있어 party의 announcement 속성이 가사의 다음 줄로 반복적으로 설정되도록 합니다.
QML에서는 BirthdayParty 내부에서 HappyBirthdaySong 가 인스턴스화됩니다. 시그니처의 on 키워드는 값 소스가 대상으로 하는 속성을 지정하는 데 사용되며, 이 경우 announcement 입니다. HappyBirthdaySong 객체의 name 속성도 파티 호스트의 이름에 바인딩되어 있습니다.
BirthdayParty {
id: party
HappyBirthdaySong on announcement {
name: party.host.name
}
...
}이 프로그램은 partyStarted 신호를 사용하여 파티가 시작된 시간을 표시한 다음, 다음과 같은 생일 축하 문구를 반복해서 출력합니다.
Happy birthday to you,
Happy birthday to you,
Happy birthday dear Bob Jones,
Happy birthday to you!외부 객체 통합
extending-qml-advanced/advanced7-Foreign-objects-integration
참석자들은 가사를 콘솔에 단순히 출력하는 대신, 색상을 지원하는 더 화려한 디스플레이를 사용하고 싶어 합니다. 이를 프로젝트에 통합하고 싶지만, 현재 해당 라이브러리가 타사 라이브러리에서 제공되므로 QML에서 화면을 구성할 수 없습니다. 이 문제를 해결하려면 필요한 타입을 QML 엔진에 노출시켜, QML 내에서 직접 해당 속성을 수정할 수 있도록 해야 합니다.
디스플레이는 ` ThirdPartyDisplay ` 클래스를 통해 제어할 수 있습니다. 이 클래스에는 표시할 텍스트의 내용과 전경색, 배경색을 정의하는 속성이 있습니다.
class Q_DECL_EXPORT ThirdPartyDisplay : public QObject
{
Q_OBJECT
Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged FINAL)
Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor NOTIFY colorsChanged FINAL)
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY colorsChanged FINAL)
...
};이 타입을 QML에 노출하려면 QML_ELEMENT 을 사용하여 엔진에 등록할 수 있습니다. 하지만 해당 클래스는 수정이 불가능하므로, QML_ELEMENT 을 단순히 추가할 수는 없습니다. 이 타입을 엔진에 등록하려면 외부에서 타입을 등록해야 합니다. 이것이 바로 ` QML_FOREIGN `의 용도입니다. 타입 내에서 다른 QML 매크로와 함께 사용될 때, 다른 매크로들은 자신이 속한 타입이 아니라 ` QML_FOREIGN`로 지정된 외부 타입에 적용됩니다.
class ForeignDisplay : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(ThirdPartyDisplay)
QML_FOREIGN(ThirdPartyDisplay)
};이렇게 하면 BirthdayParty에 display라는 새로운 속성이 생깁니다.
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL)
Q_PROPERTY(ThirdPartyDisplay *display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
...
};또한 QML에서 세 번째 디스플레이의 텍스트 색상을 명시적으로 설정할 수 있습니다.
BirthdayParty {
display: ThirdPartyDisplay {
foregroundColor: "black"
backgroundColor: "white"
}
...
}이제 BirthdayParty의 announcement 속성을 설정하면, 직접 출력하는 대신 멋진 디스플레이로 메시지가 전송됩니다.
void BirthdayParty::setAnnouncement(const QString &announcement)
{
if (m_announcement != announcement) {
m_announcement = announcement;
emit announcementChanged();
}
m_display->setContent(announcement);
}그러면 출력은 이전 섹션과 마찬가지로 다음과 같이 반복해서 표시됩니다.
[Fancy ThirdPartyDisplay] Happy birthday to you,
[Fancy ThirdPartyDisplay] Happy birthday to you,
[Fancy ThirdPartyDisplay] Happy birthday dear Bob Jones,
[Fancy ThirdPartyDisplay] Happy birthday to you!QML 객체 유형에 대한 기본 및 상위 속성 지정, 그룹화된 속성, 부가 속성 제공, 속성 값 소스 및 외부 유형 등록항목도 참조하십시오 .
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.