C++에서 QML 유형 정의하기
C++ 코드로 QML을 확장할 때, C++ 클래스를 QML 타입 시스템에 등록하여 해당 클래스를 QML 코드 내에서 데이터 타입으로 사용할 수 있게 할 수 있습니다. “QML에 C++ 유형의 속성 노출”에서 설명한 바와 같이, ` QObject`에서 파생된 모든 클래스의 속성, 메서드 및 신호는 QML에서 접근할 수 있지만, 해당 클래스는 유형 시스템에 등록되기 전까지는 QML에서 데이터 유형으로 사용할 수 없습니다. 또한 등록을 통해 클래스를 QML에서 인스턴스화 가능한 QML 객체 유형으로 사용할 수 있게 하거나, 클래스의 싱글톤 인스턴스를 QML에서 가져와 사용할 수 있게 하는 등 다른 기능들도 제공됩니다.
또한, Qt Qml 이 모듈은 C++에서 부착 속성(attached properties ) 및 기본 속성(default properties )과 같은 QML 고유 기능을 구현하기 위한 메커니즘을 제공합니다.
(이 문서에서 다루는 여러 중요한 개념은 ‘C++을 사용하여 QML 확장 작성하기 ’ 튜토리얼에서 시연되고 있습니다.)
참고: QML 유형을 선언하는 모든 헤더는 프로젝트의 포함 경로에서 접두사 없이 접근할 수 있어야 합니다.
C++ 및 다양한 QML 통합 방법에 대한 자세한 내용은 C++ 및 QML 통합 개요 페이지를 참조하십시오.
QML 유형 시스템에 C++ 유형 등록
QObject 에서 파생된 클래스는 QML 타입 시스템에 등록하여 QML 코드 내에서 데이터 타입으로 사용할 수 있습니다.
엔진은 인스턴스화 가능한 유형과 인스턴스화 불가능한 유형 모두를 등록할 수 있도록 허용합니다. 인스턴스화 가능한 유형을 등록하면 C++ 클래스를 QML 객체 유형의 정의로 사용할 수 있게 되어, QML 코드의 객체 선언에서 이 유형의 객체를 생성하는 데 사용할 수 있습니다. 또한 등록을 통해 엔진에 추가적인 유형 메타데이터가 제공되므로, 해당 유형(및 해당 클래스에서 선언된 모든 열거형)을 QML과 C++ 간에 교환되는 속성 값, 메서드 매개변수 및 반환 값, 신호 매개변수의 데이터 유형으로 사용할 수 있습니다.
인스턴스화할 수 없는 타입을 등록해도 이와 같은 방식으로 클래스가 데이터 타입으로 등록되지만, 해당 타입은 QML에서 QML 객체 타입으로 인스턴스화되어 사용될 수 없습니다. 이는 예를 들어, 타입에 QML에 노출되어야 하는 열거형이 있지만 타입 자체는 인스턴스화될 수 없어야 하는 경우에 유용합니다.
C++ 타입을 QML에 노출하기 위한 올바른 접근 방식을 선택하는 방법에 대한 간략한 안내서는 ‘C++과 QML 간의 올바른 통합 방법 선택’을 참조하십시오.
전제 조건
아래에 언급된 모든 매크로는 QtQmlIntegration 모듈의 ` qqmlintegration.h ` 헤더 파일에서 사용할 수 있습니다.
매크로를 사용할 수 있게 하려면 해당 매크로를 사용하는 파일에 다음 코드를 추가해야 합니다.
#include <QtQmlIntegration/qqmlintegration.h>이미 QtQml 모듈을 링크하고 있는 경우, 다음과 같이 qqmlintegration.h 을 포함하는 qqmlregistration.h 헤더 파일을 대신 사용할 수 있습니다:
#include <QtQml/qqmlregistration.h>또한, 클래스 선언은 프로젝트의 포함 경로를 통해 접근 가능한 헤더 파일에 위치해야 합니다. 이 선언들은 컴파일 시점에 등록 코드를 생성하는 데 사용되며, 등록 코드에는 해당 선언이 포함된 헤더 파일이 포함되어야 합니다.
인스턴스화 가능한 객체 유형 등록
QObject 에서 파생된 모든 C++ 클래스는 QML 객체 유형의 정의로 등록될 수 있습니다. 클래스가 QML 유형 시스템에 등록되면, QML 코드에서 다른 객체 유형과 마찬가지로 해당 클래스를 선언하고 인스턴스화할 수 있습니다. 생성된 클래스 인스턴스는 QML에서 조작할 수 있습니다. ‘C++ 유형의 속성을 QML에 노출하기’에서 설명한 바와 같이, ` QObject`에서 파생된 모든 클래스의 속성, 메서드 및 시그널은 QML 코드에서 접근할 수 있습니다.
QObject 에서 파생된 클래스를 인스턴스화 가능한 QML 객체 유형으로 등록하려면, 클래스 선언에 QML_ELEMENT 또는 QML_NAMED_ELEMENT(<name>) 를 추가하십시오. 또한 빌드 시스템에서도 설정을 조정해야 합니다. qmake의 경우, 프로젝트 파일에 CONFIG += qmltypes, QML_IMPORT_NAME 및 QML_IMPORT_MAJOR_VERSION 를 추가하십시오. CMake의 경우, 해당 클래스가 포함된 파일은 qt_add_qml_module()을 사용하여 타깃 설정에 포함되어야 합니다. 이렇게 하면 클래스 이름이나 명시적으로 지정된 이름을 QML 타입 이름으로 사용하여, 지정된 메이저 버전 아래의 타입 네임스페이스에 클래스가 등록됩니다. 마이너 버전은 속성, 메서드 또는 시그널에 첨부된 리비전 번호에서 파생됩니다. 기본 마이너 버전은 0 입니다. 클래스 선언에 QML_ADDED_IN_VERSION() 매크로를 추가하여 해당 타입이 특정 마이너 버전에서만 사용 가능하도록 명시적으로 제한할 수 있습니다. 클라이언트는 해당 타입을 사용하기 위해 네임스페이스의 적절한 버전을 임포트할 수 있습니다.
예를 들어, author 및 creationDate 속성을 가진 Message 클래스가 있다고 가정해 보겠습니다.
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
QML_ELEMENT
public:
// ...
};이 타입은 프로젝트 파일에 적절한 타입 네임스페이스와 버전 번호를 추가하여 등록할 수 있습니다. 예를 들어, 이 타입을 버전 1.0으로 com.mycompany.messaging 네임스페이스에서 사용할 수 있게 하려면 다음과 같이 합니다:
qt_add_qml_module(messaging
URI com.mycompany.messaging
VERSION 1.0
SOURCES
message.cpp message.h
)CONFIG += qmltypes
QML_IMPORT_NAME = com.mycompany.messaging
QML_IMPORT_MAJOR_VERSION = 1해당 클래스가 선언된 헤더 파일이 프로젝트의 포함 경로에서 접근할 수 없는 경우, 생성된 등록 코드를 컴파일할 수 있도록 포함 경로를 수정해야 할 수도 있습니다.
INCLUDEPATH += com/mycompany/messaging이 타입은 QML의 객체 선언에서 사용할 수 있으며, 아래 예제와 같이 해당 속성을 읽고 쓸 수 있습니다:
import com.mycompany.messaging
Message {
author: "Amelie"
creationDate: new Date()
}값 타입 등록
Q_GADGET 매크로가 포함된 모든 유형은 QML 값 유형으로 등록될 수 있습니다. 이러한 유형이 QML 유형 시스템에 등록되면 QML 코드에서 속성 유형으로 사용할 수 있습니다. 이러한 인스턴스는 QML에서 조작할 수 있습니다. ‘C++ 유형의 속성을 QML에 노출하기’에서 설명하듯이, 모든 값 유형의 속성과 메서드는 QML 코드에서 접근할 수 있습니다.
객체 유형과 달리, 값 유형은 소문자 이름을 사용해야 합니다. 이를 등록하는 권장 방법은 QML_VALUE_TYPE 또는 QML_ANONYMOUS 매크로를 사용하는 것입니다. C++ 클래스는 일반적으로 대문자 이름을 가지므로, QML_ELEMENT 에 해당하는 매크로는 없습니다. 그 외의 등록 절차는 객체 유형의 등록과 매우 유사합니다.
예를 들어, 이름과 성을 나타내는 두 개의 문자열로 구성된 값 유형 person 를 등록하고 싶다고 가정해 봅시다.
class Person
{
Q_GADGET
Q_PROPERTY(QString firstName READ firstName WRITE setFirstName)
Q_PROPERTY(QString lastName READ lastName WRITE setLastName)
QML_VALUE_TYPE(person)
public:
// ...
};값 타입으로 수행할 수 있는 작업에는 몇 가지 추가적인 제한 사항이 있습니다:
- 값 유형은 싱글톤이 될 수 없습니다.
- 값 유형은 기본 생성자와 복사 생성자를 가져야 합니다.
- QProperty 를 값 유형의 멤버로 사용하는 것은 문제가 있습니다. 값 유형은 복사되므로, 그 시점에서 QProperty 에 설정된 바인딩을 어떻게 처리할지 결정해야 합니다. 값 유형에서는 QProperty 를 사용해서는 안 됩니다.
- 값 유형은 부착 속성을 제공할 수 없습니다.
- 값 유형에 대한 확장(QML_EXTENDED)을 정의하는 API는 공개되지 않았으며 향후 변경될 수 있습니다.
열거형을 사용하는 값형
값 유형의 열거형을 QML에 노출하려면 몇 가지 추가 단계가 필요합니다.
QML에서 값 타입의 이름은 소문자로 표기되며, 소문자 이름을 가진 타입은 일반적으로 JavaScript 코드에서 참조할 수 없습니다( pragma ValueTypeBehavior: Addressable을 명시적으로 지정하지 않는 한). C++에 열거형을 포함하는 값 타입이 있고 이를 QML에 노출하려는 경우, 해당 열거형을 별도로 노출해야 합니다.
이는 ` QML_FOREIGN_NAMESPACE`를 사용하여 해결할 수 있습니다. 먼저, 값 유형을 상속받아 별도의 C++ 유형을 생성합니다:
class Person
{
Q_GADGET
Q_PROPERTY(QString firstName READ firstName WRITE setFirstName)
Q_PROPERTY(QString lastName READ lastName WRITE setLastName)
QML_VALUE_TYPE(person)
public:
enum TheEnum { A, B, C };
Q_ENUM(TheEnum)
//...
};
class PersonDerived: public Person
{
Q_GADGET
};그런 다음 파생된 타입을 외부 네임스페이스로 노출합니다:
namespace PersonDerivedForeign
{
Q_NAMESPACE
QML_NAMED_ELEMENT(Person)
QML_FOREIGN_NAMESPACE(PersonDerived)
}이렇게 하면 Person (대문자)라는 QML 네임스페이스가 생성되며, 이 네임스페이스에는 TheEnum 라는 열거형과 A, B, C 라는 값들이 포함됩니다. 그러면 QML에서 다음과 같이 작성할 수 있습니다:
someProperty: Person.A동시에 person (소문자)라는 값 유형도 이전과 똑같이 계속 사용할 수 있습니다.
인스턴스화할 수 없는 유형 등록
때로는 ` QObject`에서 파생된 클래스를 QML 유형 시스템에 등록해야 하지만, 인스턴스화 가능한 유형으로 등록해서는 안 되는 경우가 있습니다. 예를 들어, 다음과 같은 C++ 클래스의 경우입니다:
- 인스턴스화될 수 없어야 하는 인터페이스 유형인 경우
- QML에 노출될 필요가 없는 기저 클래스 유형인 경우
- QML에서 접근할 수 있어야 하지만, 그 외에는 인스턴스화될 수 없어야 하는 열거형을 선언하는 경우
- 싱글톤 인스턴스를 통해 QML에 제공되어야 하며, QML에서 인스턴스화되어서는 안 되는 유형인 경우
이 Qt Qml 이 모듈은 인스턴스화할 수 없는 타입을 등록하기 위한 여러 매크로를 제공합니다:
- QML_ANONYMOUS 인스턴스화할 수 없고 QML에서 참조할 수 없는 C++ 타입을 등록합니다. 이를 통해 엔진은 QML에서 인스턴스화할 수 있는 상속된 타입을 강제 변환할 수 있습니다.
- QML_INTERFACE 기존 Qt 인터페이스 타입을 등록합니다. 이 타입은 QML에서 인스턴스화할 수 없으며, 이를 사용하여 QML 속성을 선언할 수도 없습니다. 하지만 QML에서 이 타입의 C++ 속성을 사용하면 예상되는 인터페이스 형변환이 수행됩니다.
- QML_UNCREATABLE(reason)을 QML_ELEMENT 또는 QML_NAMED_ELEMENT 와 함께 사용하면, 인스턴스화할 수는 없지만 QML 타입 시스템에서 타입으로 식별되어야 하는 명명된 C++ 타입을 등록합니다. 이는 타입의 열거형이나 부착된 속성은 QML에서 접근할 수 있어야 하지만, 타입 자체는 인스턴스화할 수 없어야 하는 경우에 유용합니다. 이 매개변수는 해당 타입의 인스턴스 생성 시도가 감지될 경우 출력될 오류 메시지여야 합니다.
- QML_SINGLETON QML_ELEMENT 또는 와 함께 사용하면, 아래에서 설명하듯이 QML에서 가져올 수 있는 싱글톤 타입을 등록합니다. QML_NAMED_ELEMENT
QML 유형 시스템에 등록된 모든 C++ 유형은 인스턴스화할 수 없는 유형이라 하더라도 반드시 QObject 에서 파생되어야 한다는 점에 유의하십시오.
싱글톤 타입을 사용하여 싱글톤 객체 등록하기
싱글톤 유형을 사용하면 클라이언트가 객체 인스턴스를 수동으로 생성할 필요 없이 네임스페이스 내에서 속성, 시그널 및 메서드를 노출할 수 있습니다. 특히 ` QObject ` 싱글톤 유형은 기능이나 전역 속성 값을 제공하는 효율적이고 편리한 방법입니다.
싱글톤 타입은 엔진 내의 모든 컨텍스트에서 공유되므로, 관련 QQmlContext 가 없다는 점에 유의하십시오. QObject 싱글톤 타입 인스턴스는 QQmlEngine 에 의해 생성 및 소유되며, 엔진이 소멸될 때 함께 소멸됩니다.
QObject 싱글톤 타입은 다른 QObject 또는 인스턴스화된 타입과 유사한 방식으로 상호 작용할 수 있지만, 단 하나의 인스턴스(엔진이 생성하고 소유하는)만 존재하며, ID가 아닌 타입 이름으로 참조해야 한다는 점이 다릅니다. QObject 의 싱글톤 타입에 대한 Q_PROPERTY는 바인딩될 수 있으며, QObject 모듈 API의 Q_INVOKABLE 함수는 시그널 핸들러 표현식에서 사용될 수 있습니다. 이로 인해 싱글톤 타입은 스타일링이나 테마 구현에 이상적인 방법이며, 전역 상태를 저장하거나 전역 기능을 제공하기 위해 ".pragma library" 스크립트 임포트 대신 사용할 수도 있습니다.
일단 등록되면, QObject 싱글톤 유형은 QML에 노출된 다른 QObject 인스턴스와 마찬가지로 임포트하여 사용할 수 있습니다. 다음 예제는 QObject 싱글톤 유형이 버전 1.0으로 "MyThemeModule" 네임스페이스에 등록되었다고 가정하며, 해당 QObject 에는 QColor "color" Q_PROPERTY 속성이 있습니다:
import MyThemeModule 1.0 as Theme
Rectangle {
color: Theme.color // binding.
}QJSValue 도 싱글톤 유형으로 노출될 수 있지만, 클라이언트는 이러한 싱글톤 유형의 속성에는 바인딩할 수 없다는 점을 유의해야 합니다.
새로운 싱글톤 유형을 구현하고 등록하는 방법, 그리고 기존 싱글톤 유형을 사용하는 방법에 대한 자세한 내용은 QML_SINGLETON을 참조하십시오. 싱글톤에 대한 보다 심층적인 정보는 QML의 싱글톤을 참조하십시오.
참고: QML에서 등록된 유형의열거형 값은 대문자로 시작해야 합니다.
최종 속성
Q_PROPERTY 에 ` FINAL ` 수식자를 사용하여 `final`로 선언된 속성은 재정의할 수 없습니다. 즉, QML이나 C++에서 파생 유형에 선언된 동일한 이름의 속성이나 함수는 QML 엔진에 의해 무시됩니다. 의도치 않은 재정의가 발생하지 않도록 가능한 경우 속성을 ` FINAL `로 선언해야 합니다. 속성의 재정의는 파생 클래스뿐만 아니라, 기저 클래스의 컨텍스트를 실행하는 QML 코드에서도 인식됩니다. 하지만 이러한 QML 코드는 일반적으로 원래의 속성을 기대합니다. 이는 실수의 빈번한 원인이 됩니다.
FINAL 로 선언된 속성은 QML의 함수나 C++의 Q_INVOKABLE 메서드를 통해서도 재정의할 수 없습니다.
유형 개정 및 버전
많은 유형 등록 함수는 등록된 유형에 대한 버전을 지정하도록 요구합니다. 유형 개정판과 버전을 통해 이전 버전과의 호환성을 유지하면서 새 버전에 새로운 속성이나 메서드를 추가할 수 있습니다.
다음 두 개의 QML 파일을 살펴보겠습니다:
// main.qml
import QtQuick 1.0
Item {
id: root
MyType {}
}// MyType.qml
import MyTypes 1.0
CppType {
value: root.x
}여기서 ` CppType `는 C++ 클래스 ` CppType`에 매핑됩니다.
CppType의 작성자가 새로운 버전의 타입 정의에서 CppType에 root 속성을 추가하면, root 도 최상위 구성 요소의 id 이므로 root.x 는 이제 다른 값으로 해결됩니다. 작성자는 새로운 root 속성이 특정 마이너 버전부터 사용 가능하도록 지정할 수 있습니다. 이를 통해 기존 프로그램을 깨뜨리지 않고도 기존 타입에 새로운 속성과 기능을 추가할 수 있습니다.
REVISION 태그는 root 속성이 해당 타입의 개정판 1에서 추가되었음을 표시하는 데 사용됩니다. Q_INVOKABLE 의 메서드, 시그널 및 슬롯과 같은 요소들도 Q_REVISION 매크로를 사용하여 개정판에 태그를 지정할 수 있습니다:
class CppType : public BaseType
{
Q_OBJECT
Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION(1, 0))
QML_ELEMENT
signals:
Q_REVISION(1, 0) void rootChanged();
};이러한 방식으로 지정된 리비전은 프로젝트 파일에 명시된 메이저 버전에 대한 마이너 버전으로 자동 해석됩니다. 이 경우, ` root `는 ` MyTypes ` 버전 1.1 이상이 임포트된 경우에만 사용할 수 있습니다. ` MyTypes ` 버전 1.0의 임포트에는 영향을 미치지 않습니다.
같은 이유로, 이후 버전에서 도입된 새로운 유형에는 QML_ADDED_IN_VERSION 매크로를 지정해야 합니다.
이 언어 기능은 기존 애플리케이션을 깨뜨리지 않고도 동작상의 변경을 적용할 수 있게 해줍니다. 따라서 QML 모듈 작성자는 항상 소수 버전 간에 변경된 사항을 문서화해야 하며, QML 모듈 사용자는 업데이트된 임포트 문장을 적용하기 전에 자신의 애플리케이션이 여전히 올바르게 실행되는지 확인해야 합니다.
해당 타입이 의존하는 기본 클래스의 수정 사항은 타입 자체를 등록할 때 자동으로 등록됩니다. 이는 다른 작성자가 제공한 기본 클래스에서 파생할 때, 예를 들어 Qt Quick 모듈의 클래스를 확장할 때 유용합니다.
참고: QML엔진은 그룹화되거나 부착된 속성 객체의 속성이나 신호에 대한 개정 버전을 지원하지 않습니다.
확장 객체 등록
기존 클래스와 기술을 QML에 통합할 때, 선언적 환경에 더 잘 부합하도록 API를 조정해야 하는 경우가 종종 있습니다. 일반적으로 원본 클래스를 직접 수정하는 것이 최상의 결과를 가져오지만, 이것이 불가능하거나 다른 문제로 인해 복잡해지는 경우, 확장 객체를 사용하면 직접 수정하지 않고도 사용자가 제어하는 타입에 대해 제한적인 확장 기능을 제공할 수 있습니다. Qt 자체 타입의 확장은 지원되지 않습니다.
확장 객체는 기존 타입에 추가 속성을 부여합니다. 확장된 타입 정의를 사용하면 프로그래머가 클래스를 등록할 때 ‘확장 타입’이라 하는 추가 타입을 지정할 수 있습니다. 이 타입의 멤버는 QML 내에서 사용될 때 원래 대상 클래스와 투명하게 병합됩니다. 예를 들어:
QLineEdit {
leftMargin: 20
}leftMargin 속성은 소스 코드를 수정하지 않고도 기존 C++ 타입인 QLineEdit 에 추가된 새로운 속성입니다.
QML_EXTENDED(확장) 매크로는 확장 유형을 등록하기 위한 것입니다. 인수는 확장으로 사용될 다른 클래스의 이름입니다.
또한 ` QML_EXTENDED_NAMESPACE(namespace)`를 사용하여 네임스페이스, 특히 그 안에 선언된 열거형을 유형의 확장으로 등록할 수도 있습니다. 확장하려는 유형 자체가 네임스페이스인 경우에는 대신 ` QML_NAMESPACE_EXTENDED(namespace)`를 사용해야 합니다.
확장 클래스는 QObject 를 상속받은 일반적인 QObject 이며, 포인터를 매개변수로 받는 생성자를 가집니다. 그러나 확장 클래스의 생성은 첫 번째 확장 속성에 접근할 때까지 지연됩니다. 확장 클래스가 생성되면 대상 객체가 부모로 전달됩니다. 원본 객체의 속성에 접근하면 대신 확장 객체의 해당 속성이 사용됩니다.
외부 유형 등록
앞서 언급한 매크로를 포함하도록 수정할 수 없는 C++ 유형이 있을 수 있습니다. 이러한 유형은 타사 라이브러리의 유형이거나, 해당 매크로의 존재와 상충되는 특정 계약을 충족해야 하는 유형일 수 있습니다. 하지만 ` QML_FOREIGN ` 매크로를 사용하면 이러한 타입을 QML에 노출할 수 있습니다. 이를 위해서는 다음과 같이 등록 매크로로만 구성된 별도의 구조체(struct)를 생성하면 됩니다:
// Contains class Immutable3rdParty
#include <3rdpartyheader.h>
struct Foreign
{
Q_GADGET
QML_FOREIGN(Immutable3rdParty)
QML_NAMED_ELEMENT(Accessible3rdParty)
QML_ADDED_IN_VERSION(2, 4)
// QML_EXTENDED, QML_SINGLETON ...
};이 코드를 통해 Immutable3rdParty의 메서드와 속성을 가지며, Foreign에 지정된 QML 트레이트(예: singleton, extended)를 갖춘 QML 타입을 얻을 수 있습니다.
QML 전용 타입 및 속성 정의
부착 속성 제공
QML 언어 구문에는 객체에 부착되는 추가 속성인 ‘부착 속성’ 과 ‘부착 신호 핸들러’라는 개념이 있습니다. 본질적으로 이러한 속성은 부착 유형에 의해 구현 및 제공되며, 다른 유형의 객체에 부착될 수 있습니다. 이는 객체 유형 자체(또는 객체가 상속받은 유형)에 의해 제공되는 일반적인 객체 속성과는 대조적입니다.
예를 들어, 아래의 ` Item `은 부착 속성과 부착 핸들러를 사용합니다:
import QtQuick 2.0
Item {
width: 100; height: 100
focus: true
Keys.enabled: false
Keys.onReturnPressed: console.log("Return key was pressed")
}여기서 ` Item ` 객체는 ` Keys.enabled ` 및 ` Keys.onReturnPressed`의 값에 접근하고 설정할 수 있습니다. 이를 통해 ` Item ` 객체는 이러한 추가 속성을 자체 기존 속성의 확장으로 접근할 수 있습니다.
부착된 객체 구현 단계
위의 예제를 살펴보면, 여기에는 여러 주체가 관여되어 있습니다:
enabled속성과returnPressed신호를 가진 익명의 부착 객체 유형의 인스턴스가 있으며, 이 인스턴스는 Item 객체에 부착되어 해당 객체가 이러한 속성에 접근하고 설정할 수 있도록 합니다.- Item 객체는 부착 대상( attachee)으로, 부착된 객체 유형의 인스턴스가 이 객체에 부착되었습니다.
- Keys 는 부착 유형으로, 부착 대상에게 "Keys"라는 명명된 한정자를 제공하며, 이를 통해 부착 대상은 부착된 객체 유형의 속성에 접근할 수 있습니다.
QML 엔진이 이 코드를 처리할 때, 부착된 객체 유형의 인스턴스 하나를 생성하고 이 인스턴스를 Item 객체에 부착함으로써, 해당 객체가 인스턴스의 enabled 및 returnPressed 속성에 접근할 수 있도록 합니다.
부착된 객체를 제공하는 메커니즘은 C++에서 부착된 객체 유형 및 부착 유형에 대한 클래스를 제공함으로써 구현할 수 있습니다. 부착된 객체 유형의 경우, 부착 대상 객체가 접근할 수 있도록 할 속성을 정의하는 ` QObject`에서 파생된 클래스를 제공해야 합니다. 부착 유형의 경우, 다음 조건을 충족하는 ` QObject`에서 파생된 클래스를 제공해야 합니다:
- 다음과 같은 시그니처를 가진 정적 qmlAttachedProperties() 메서드를 구현해야 합니다:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);이 메서드는 연결된 객체 유형의 인스턴스를 반환해야 합니다.
QML 엔진은 `
object` 매개변수로 지정된 대상 객체에 해당 객체 유형의 인스턴스를 연결하기 위해 이 메서드를 호출합니다. 엄격히 요구되지는 않지만, 메모리 누수를 방지하기 위해 이 메서드 구현에서 반환된 인스턴스의 부모로 `object`를 지정하는 것이 관례입니다.엔진은 이후의 부착된 속성 액세스를 위해 반환된 인스턴스 포인터를 캐시하므로, 각 부착 대상 객체 인스턴스에 대해 이 메서드는 엔진에 의해 최대 한 번만 호출됩니다. 따라서 부착 대상의
object가 소멸될 때까지 부착 객체는 삭제될 수 없습니다. - QML_ATTACHED(attached) 매크로를 클래스 선언에 추가하여 `attachee`를 부착 유형으로 선언합니다. 인수는 부착된 객체 유형의 이름입니다.
부착 객체 구현: 예제
예를 들어, 앞서 예제에서 설명한 Message 타입을 살펴보겠습니다:
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
QML_ELEMENT
public:
// ...
};Message 가 메시지 보드에 게시될 때 신호를 트리거하고, 또한 메시지 보드에서 메시지가 만료된 시점을 추적해야 한다고 가정해 봅시다. 이러한 속성은 메시지 보드( Message)의 컨텍스트와 더 밀접한 관련이 있으므로, 메시지 보드 객체( )에 직접 추가하는 것은 타당하지 않습니다. 따라서 이 속성들은 “MessageBoard” 한정자를 통해 제공되는 메시지 보드 객체( Message )의 부가 속성( attached attributes)으로 구현될 수 있습니다. 앞서 설명한 개념에 비추어 볼 때, 여기에 관련된 당사자들은 다음과 같습니다:
published신호와expired속성을 제공하는 익명의 부가 객체 유형의 인스턴스입니다. 이 유형은 아래의MessageBoardAttachedType에 의해 구현됩니다.Message객체로, 이는 부착 대상이 됩니다.- `
MessageBoard` 유형으로, `Message` 객체가 연결된 속성에 접근하는 데 사용되는 연결 유형이 됩니다.
다음은 구현 예시입니다. 먼저, 연결 대상( attachee)이 접근할 수 있는 필수 속성과 신호를 가진 연결된 객체(attached object) 유형이 있어야 합니다:
class MessageBoardAttachedType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged)
QML_ANONYMOUS
public:
MessageBoardAttachedType(QObject *parent);
bool expired() const;
void setExpired(bool expired);
signals:
void published();
void expiredChanged();
};그런 다음, 부착 유형인` MessageBoard` 은`MessageBoardAttachedType`에서 구현된 대로 부착된 객체 유형의 인스턴스를 반환하는 ` qmlAttachedProperties() ` 메서드를 선언해야 합니다. 또한, ` MessageBoard `은 ` QML_ATTACHED()` 매크로를 통해 부착 유형으로 선언되어야 합니다:
class MessageBoard : public QObject
{
Q_OBJECT
QML_ATTACHED(MessageBoardAttachedType)
QML_ELEMENT
public:
static MessageBoardAttachedType *qmlAttachedProperties(QObject *object)
{
return new MessageBoardAttachedType(object);
}
};이제 ` Message ` 타입은 연결된 객체 타입의 속성과 시그널에 접근할 수 있습니다:
Message {
author: "Amelie"
creationDate: new Date()
MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00")
MessageBoard.onPublished: console.log("Message by", author, "has been
published!")
}또한, C++ 구현체는 qmlAttachedPropertiesObject() 함수를 호출하여 어떤 객체에든 연결된 연결된 객체 인스턴스에 접근할 수 있습니다.
예를 들어:
Message *msg = someMessageInstance();
MessageBoardAttachedType *attached =
qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg));
qDebug() << "Value of MessageBoard.expired:" << attached->expired();부착 속성의 전파
QQuickAttachedPropertyPropagator font 및 의 전파 방식과 유사하게, 부모 객체에서 자식 객체로 첨부 속성을 전파하기 위해 서브클래스를 생성할 수 있습니다. 이 기능은 , , 를 통한 전파를 지원합니다. palette items popups windows
속성 수정자 유형
속성 수정자 유형은 특수한 종류의 QML 객체 유형입니다. 속성 수정자 유형 인스턴스는 적용된 (QML 객체 인스턴스의) 속성에 영향을 미칩니다. 속성 수정자 유형에는 두 가지 종류가 있습니다:
- 속성 값 쓰기 인터셉터
- 속성 값 소스
속성 값 쓰기 인터셉터는 속성에 값이 기록될 때 해당 값을 필터링하거나 수정하는 데 사용할 수 있습니다. 현재 지원되는 유일한 속성 값 쓰기 인터셉터는 QtQuick 임포트에서 제공하는 Behavior 유형입니다.
속성 값 소스는 시간이 지남에 따라 속성의 값을 자동으로 업데이트하는 데 사용할 수 있습니다. 클라이언트는 자체 속성 값 소스 유형을 정의할 수 있습니다. QtQuick 임포트가 제공하는 다양한 속성 애니메이션 유형은 속성 값 소스의 예시입니다.
다음 예제에서 볼 수 있듯이, “<ModifierType> on <propertyName>” 구문을 통해 속성 수정자 유형 인스턴스를 생성하고 QML 객체의 속성에 적용할 수 있습니다.
import QtQuick 2.0
Item {
width: 400
height: 50
Rectangle {
width: 50
height: 50
color: "red"
NumberAnimation on x {
from: 0
to: 350
loops: Animation.Infinite
duration: 2000
}
}
}이를 일반적으로 “on” 구문이라고 합니다.
클라이언트는 자체 속성 값 소스 유형을 등록할 수 있지만, 현재로서는 속성 값 쓰기 인터셉터는 등록할 수 없습니다.
속성 값 소스
속성 값 소스는 <PropertyValueSource> on <property> 구문을 사용하여 시간이 지남에 따라 속성의 값을 자동으로 업데이트할 수 있는 QML 유형입니다. 예를 들어, QtQuick 모듈에서 제공하는 다양한 속성 애니메이션 유형은 속성 값 소스의 예입니다.
속성 값 소스는 QQmlPropertyValueSource 를 상속받은 C++ 클래스를 구현하고, 시간이 지남에 따라 속성에 다양한 값을 기록하는 구현을 제공함으로써 구현할 수 있습니다. QML에서 <PropertyValueSource> on <property> 구문을 사용하여 속성 값 소스를 속성에 적용하면, 엔진에서 해당 속성에 대한 참조를 제공하여 속성 값이 업데이트될 수 있도록 합니다.
예를 들어, 속성 값 소스로 사용될 ` RandomNumberGenerator ` 클래스가 있다고 가정해 봅시다. 이 클래스는 QML 속성에 적용될 때 500밀리초마다 속성 값을 서로 다른 난수로 업데이트합니다. 또한 이 난수 생성기에 `maxValue`를 지정할 수도 있습니다. 이 클래스는 다음과 같이 구현할 수 있습니다:
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_INTERFACES(QQmlPropertyValueSource)
Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged);
QML_ELEMENT
public:
RandomNumberGenerator(QObject *parent)
: QObject(parent), m_maxValue(100)
{
QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty()));
m_timer.start(500);
}
int maxValue() const;
void setMaxValue(int maxValue);
virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; }
signals:
void maxValueChanged();
private slots:
void updateProperty() {
m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue));
}
private:
QQmlProperty m_targetProperty;
QTimer m_timer;
int m_maxValue;
};QML 엔진이 ` RandomNumberGenerator `를 속성 값 소스로 사용하는 부분을 감지하면, ` RandomNumberGenerator::setTarget() `를 호출하여 값 소스가 적용된 속성이 있는 타입을 전달합니다. ` RandomNumberGenerator `의 내부 타이머가 500밀리초마다 트리거되면, 해당 지정된 속성에 새로운 숫자 값을 기록합니다.
RandomNumberGenerator 클래스가 QML 유형 시스템에 등록되면, QML에서 속성 값 소스로 사용할 수 있습니다. 아래 예제에서는 이 클래스를 사용하여 Rectangle 의 너비를 500밀리초마다 변경하고 있습니다:
import QtQuick 2.0
Item {
width: 300; height: 300
Rectangle {
RandomNumberGenerator on width { maxValue: 300 }
height: 100
color: "red"
}
}그 외 모든 측면에서 속성 값 소스는 속성, 신호, 메서드 등을 가질 수 있는 일반적인 QML 유형과 동일하지만, <PropertyValueSource> on <property> 구문을 사용하여 속성 값을 변경할 수 있는 추가 기능이 있습니다.
속성 값 소스 객체가 속성에 할당될 때, QML은 먼저 이를 일반 QML 유형인 것처럼 정상적으로 할당하려고 시도합니다. 이 할당이 실패할 경우에만 엔진이 ` setTarget()` 메서드를 호출합니다. 이를 통해 해당 유형을 단순한 값 소스 이외의 다른 맥락에서도 사용할 수 있습니다.
QML 객체 유형에 대한 기본 속성 및 상위 속성 지정
QObject 에서 파생된 유형 중 인스턴스화 가능한 QML 객체 유형으로 등록된 것은 선택적으로 해당 유형에 대한 기본 속성을 지정할 수 있습니다. 기본 속성은 객체의 자식 노드가 특정 속성에 할당되지 않은 경우 자동으로 할당되는 속성입니다.
기본 속성은 특정 "DefaultProperty" 값을 사용하여 클래스에 대해 Q_CLASSINFO() 매크로를 호출함으로써 설정할 수 있습니다. 예를 들어, 아래의 MessageBoard 클래스는 messages 속성을 클래스의 기본 속성으로 지정합니다:
class MessageBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
Q_CLASSINFO("DefaultProperty", "messages")
QML_ELEMENT
public:
QQmlListProperty<Message> messages();
private:
QList<Message *> m_messages;
};이를 통해 MessageBoard 객체의 자식 객체들이 특정 속성에 할당되지 않은 경우, 자동으로 해당 객체의 messages 속성에 할당될 수 있습니다. 예를 들어:
MessageBoard {
Message { author: "Naomi" }
Message { author: "Clancy" }
}만약 messages 가 기본 속성으로 설정되지 않았다면, 모든 Message 객체는 다음과 같이 messages 속성에 명시적으로 할당되어야 합니다:
MessageBoard {
messages: [
Message { author: "Naomi" },
Message { author: "Clancy" }
]
}(참고로, Item::data 속성은 해당 객체의 기본 속성입니다. 이 data 속성에 추가된 모든 Item 객체는 Item::children 목록에도 추가되므로, 기본 속성을 사용하면 children 속성에 명시적으로 할당하지 않고도 항목에 대한 시각적 자식 객체를 선언할 수 있습니다.)
또한, "ParentProperty" Q_CLASSINFO()를 선언하여 QML 엔진에 QML 계층 구조에서 어떤 속성이 부모 객체를 나타내야 하는지 알릴 수 있습니다. 예를 들어, Message 유형은 다음과 같이 선언될 수 있습니다:
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* board READ board BINDABLE boardBindable)
Q_PROPERTY(QString author READ author BINDABLE authorBindable)
Q_CLASSINFO("ParentProperty", "board")
QML_ELEMENT
public:
Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; }
QObject *board() const { return m_board.value(); }
QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); }
QString author() const { return m_author.value(); }
QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); }
private:
QProperty<QObject *> m_board;
QProperty<QString> m_author;
};부모 속성을 정의하면 qmllint 및 기타 도구가 코드의 의도를 더 잘 파악할 수 있으며, 일부 속성 접근 시 발생하는 오탐지 경고를 방지할 수 있습니다.
Qt Quick 모듈을 사용하여 시각적 항목 정의하기
`xml-ph-0000@deepl.internal` 모듈을 사용하여 사용자 인터페이스를 구축할 때는 Qt Quick 모듈을 사용하여 사용자 인터페이스를 구축할 때, 시각적으로 렌더링될 모든 QML 객체는 Item 유형을 상속받아야 합니다. 이는 해당 모듈 내 모든 시각적 객체의 기본 유형이기 때문입니다 Qt Quick. 이 ` Item ` 유형은 ` QQuickItem ` C++ 클래스에 의해 구현되며, 이 클래스는 `module`에서 제공됩니다. Qt Quick 모듈에서 제공됩니다. 따라서 QML 기반 사용자 인터페이스에 통합될 수 있는 시각적 유형을 C++로 구현해야 할 때는 이 클래스를 상속받아 서브클래스를 만들어야 합니다.
자세한 내용은 QQuickItem 문서를 참조하십시오. 또한, ‘C++를 사용한 QML 확장 작성’ 튜토리얼에서는 QQuickItem 기반 시각적 항목을 C++로 구현하고 Qt Quick 기반 사용자 인터페이스에 통합하는 방법을 보여줍니다.
객체 초기화에 대한 알림 수신
일부 사용자 정의 QML 객체 유형의 경우, 객체가 생성되고 모든 속성이 설정될 때까지 특정 데이터의 초기화를 지연시키는 것이 유용할 수 있습니다. 예를 들어, 초기화에 많은 비용이 소요되거나 모든 속성 값이 초기화될 때까지 초기화를 수행해서는 안 되는 경우에 해당할 수 있습니다.
` Qt Qml 모듈은 이러한 목적을 위해 상속받을 수 있는 ` QQmlParserStatus `을 제공합니다. 이 클래스는 컴포넌트 인스턴스화 과정의 다양한 단계에서 호출되는 여러 가상 메서드를 정의합니다. 이러한 알림을 수신하려면 C++ 클래스가 ` QQmlParserStatus `을 상속받고, ` Q_INTERFACES()` 매크로를 사용하여 Qt 메타 시스템에 알리도록 해야 합니다.
예를 들어:
class MyQmlType : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
QML_ELEMENT
public:
virtual void componentComplete()
{
// Perform some initialization here now that the object is fully created
}
};© 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.