C++에서 QML 타입 정의하기

C++ 코드로 QML을 확장할 때 C++ 클래스를 QML 유형 시스템에 등록하여 해당 클래스를 QML 코드 내에서 데이터 유형으로 사용할 수 있도록 할 수 있습니다. QObject - 파생 클래스의 속성, 메서드 및 신호는 QML에서 액세스할 수 있지만, C++ 타입의 속성을 QML에 노출하기에서 설명한 대로 이러한 클래스는 타입 시스템에 등록되기 전까지는 QML에서 데이터 타입으로 사용할 수 없습니다. 또한 등록을 통해 클래스를 QML에서 인스턴스화 가능한 QML 객체 유형으로 사용하거나 클래스의 싱글톤 인스턴스를 QML에서 가져와 사용할 수 있도록 하는 등의 다른 기능을 제공할 수 있습니다.

또한 Qt Qml 모듈은 첨부 프로퍼티기본 프로퍼티와 같은 QML 관련 기능을 C++에서 구현하기 위한 메커니즘을 제공합니다.

(이 문서에서 다루는 여러 가지 중요한 개념은 C++로 QML 확장 작성 튜토리얼에서 설명합니다.)

참고: QML 유형을 선언하는 모든 헤더는 프로젝트의 include 경로에서 접두사 없이 액세스할 수 있어야 합니다.

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>

또한 클래스 선언은 프로젝트의 include 경로를 통해 접근할 수 있는 헤더에 있어야 합니다. 선언은 컴파일 시 등록 코드를 생성하는 데 사용되며, 등록 코드에는 선언이 포함된 헤더가 포함되어야 합니다.

인스턴스화 가능한 객체 유형 등록하기

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() 매크로를 추가하여 특정 부 버전에서만 유형을 사용할 수 있도록 명시적으로 제한할 수 있습니다. 클라이언트는 해당 유형을 사용하기 위해 적절한 버전의 네임스페이스를 가져올 수 있습니다.

예를 들어 authorcreationDate 속성을 가진 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:
    // ...
};

이 유형은 프로젝트 파일에 적절한 유형 네임스페이스와 버전 번호를 추가하여 등록할 수 있습니다. 예를 들어, com.mycompany.messaging 네임스페이스에서 버전 1.0으로 유형을 사용할 수 있게 하려면 다음과 같이 하세요:

qmake
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

클래스가 선언된 헤더가 프로젝트의 include 경로에서 액세스할 수 없는 경우 생성된 등록 코드가 컴파일될 수 있도록 include 경로를 수정해야 할 수 있습니다.

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 을 사용해서는 안 됩니다.
  • 값 유형은 첨부된 속성을 제공할 수 없습니다.
  • 값 유형에 대한 확장을 정의하는 API(QML_EXTENDED)는 공개되지 않았으며 향후 변경될 수 있습니다.

열거형이 있는 값 유형

값 유형에서 열거형을 QML에 노출하려면 몇 가지 추가 단계가 필요합니다.

값 타입은 QML에서 소문자 이름을 가지며, 소문자 이름을 가진 타입은 일반적으로 JavaScript 코드에서 주소 지정이 불가능합니다( ValueTypeBehavior: Addressable 프라그마를 지정하지 않는 한). QML에 노출하려는 열거형이 있는 C++의 값 유형이 있는 경우 열거형을 별도로 노출해야 합니다.

이 문제는 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)
}

이렇게 하면 TheEnum 이라는 열거형과 A, B, C 라는 값이 있는 Person (대문자)라는 QML 네임스페이스가 생성됩니다. 그런 다음 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_NAMED_ELEMENT 과 결합하면 아래에 설명된 대로 QML에서 가져올 수 있는 싱글톤 타입이 등록됩니다.

QML 유형 시스템에 등록된 모든 C++ 유형은 인스턴스화되지 않더라도 QObject- 파생형이어야 합니다.

싱글톤 타입으로 싱글톤 객체 등록하기

싱글톤 유형을 사용하면 클라이언트가 객체 인스턴스를 수동으로 인스턴스화할 필요 없이 네임스페이스에 속성, 시그널 및 메서드를 노출할 수 있습니다. QObject 싱글톤 유형은 특히 기능 또는 전역 속성 값을 제공하는 효율적이고 편리한 방법입니다.

QQmlContext 싱글톤 유형은 엔진의 모든 컨텍스트에서 공유되므로 QObject 싱글톤 유형 인스턴스는 QQmlEngine 에 의해 생성되고 소유되며 엔진이 파괴될 때 파괴됩니다.

QObject 싱글톤 유형은 다른 QObject 또는 인스턴스화된 유형과 유사한 방식으로 상호 작용할 수 있지만, 단 하나의 (엔진이 생성하고 소유한) 인스턴스만 존재하며 ID가 아닌 유형 이름으로 참조해야 한다는 점이 다릅니다. QObject 싱글톤 타입의 Q_PROPERTY를 바인딩할 수 있으며, QObject 모듈 API의 Q_INVOKABLE 함수를 시그널 핸들러 표현식에 사용할 수 있습니다. 따라서 싱글톤 타입은 스타일링이나 테마를 구현하는 데 이상적인 방법이며, 전역 상태를 저장하거나 전역 기능을 제공하기 위해 ".pragma 라이브러리" 스크립트 가져오기 대신 사용할 수도 있습니다.

일단 등록되면 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_PROPERTYFINAL 수정자를 사용하여 최종적으로 선언된 프로퍼티는 재정의할 수 없습니다. 즉, 파생된 유형에서 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 프로퍼티를 특정 부 버전에서 사용할 수 있도록 지정할 수 있습니다. 이렇게 하면 기존 프로그램을 손상시키지 않고 기존 유형에 새로운 프로퍼티와 기능을 추가할 수 있습니다.

개정 태그는 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();
};

이렇게 지정된 리비전은 프로젝트 파일에 지정된 메이저 버전에 대한 마이너 버전으로 자동 해석됩니다. 이 경우 rootMyTypes 버전 1.1 이상을 가져올 때만 사용할 수 있습니다. MyTypes 버전 1.0 가져오기는 영향을 받지 않습니다.

같은 이유로 이후 버전에서 도입된 새로운 유형은 QML_ADDED_IN_VERSION 매크로로 태그를 지정해야 합니다.

이 언어의 이 기능을 사용하면 기존 애플리케이션을 중단하지 않고도 동작을 변경할 수 있습니다. 따라서 QML 모듈 작성자는 항상 마이너 버전 간에 변경된 사항을 문서화해야 하며, QML 모듈 사용자는 업데이트된 가져오기 문을 배포하기 전에 애플리케이션이 여전히 올바르게 실행되는지 확인해야 합니다.

유형이 의존하는 베이스 클래스의 개정판은 유형 자체를 등록할 때 자동으로 등록됩니다. 이는 다른 작성자가 제공한 베이스 클래스에서 파생할 때(예: Qt Quick 모듈에서 클래스를 확장할 때) 유용합니다.

참고: QML 엔진은 그룹화 및 첨부된 속성 객체의 속성 또는 시그널에 대한 리비전을 지원하지 않습니다.

확장 객체 등록하기

기존 클래스와 기술을 QML에 통합할 때 선언적 환경에 더 잘 맞도록 API를 조정해야 하는 경우가 종종 있습니다. 일반적으로 원본 클래스를 직접 수정하면 최상의 결과를 얻을 수 있지만, 이것이 불가능하거나 다른 문제로 인해 복잡한 경우 확장 객체를 사용하면 직접 수정하지 않고도 제한된 확장 가능성을 허용합니다.

확장 객체는 기존 유형에 추가 속성을 추가합니다. 확장 유형 정의를 사용하면 프로그래머가 클래스를 등록할 때 확장 유형이라고 하는 추가 유형을 제공할 수 있습니다. 그 멤버는 QML 내에서 사용할 때 원래 대상 클래스와 투명하게 병합됩니다. 예를 들어

QLineEdit {
    leftMargin: 20
}

leftMargin 속성은 소스 코드를 수정하지 않고 기존 C++ 유형인 QLineEdit 에 추가된 새 속성입니다.

QML_EXTENDED(확장) 매크로는 확장된 유형을 등록하는 데 사용됩니다. 인수는 확장으로 사용할 다른 클래스의 이름입니다.

QML_EXTENDED_NAMESPACE(네임스페이스)를 사용하여 네임스페이스, 특히 그 안에 선언된 열거형을 타입의 확장으로 등록할 수도 있습니다. 확장하려는 유형 자체가 네임스페이스인 경우에는 QML_NAMESPACE_EXTENDED(네임스페이스)를 대신 사용해야 합니다.

확장 클래스는 QObject 포인터를 받는 생성자가 있는 일반 QObject 입니다. 그러나 확장 클래스 생성은 첫 번째 확장 프로퍼티가 액세스될 때까지 지연됩니다. 확장 클래스가 생성되고 대상 객체가 부모로 전달됩니다. 원본의 프로퍼티에 액세스하면 확장 객체의 해당 프로퍼티가 대신 사용됩니다.

외부 유형 등록하기

위에서 언급한 매크로를 담기 위해 수정할 수 없는 C++ 타입이 있을 수 있습니다. 이러한 유형은 타사 라이브러리의 유형이거나 해당 매크로의 존재와 모순되는 계약을 이행해야 하는 유형일 수 있습니다. 하지만 QML_FOREIGN 매크로를 사용하여 이러한 유형을 QML에 노출할 수 있습니다. 이렇게 하려면 다음과 같이 등록 매크로로만 구성된 별도의 구조체를 만듭니다:

// 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 특성(예: 싱글톤, 확장)이 포함된 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.enabledKeys.onReturnPressed 의 값에 액세스하고 설정할 수 있습니다. 이를 통해 Item 객체는 자체 기존 속성의 확장으로 이러한 추가 속성에 액세스할 수 있습니다.

첨부된 객체를 구현하는 단계

위의 예시를 고려할 때 여러 당사자가 관련되어 있습니다:

  • enabled 속성과 returnPressed 신호가 있는 익명 첨부된 개체 유형의 인스턴스가 Item 개체에 첨부되어 이러한 속성에 액세스하고 설정할 수 있습니다.
  • Item 객체는 첨부된 객체 유형의 인스턴스가 첨부된 어태치입니다.
  • Keys 는 첨부된 객체 유형의 속성에 액세스할 수 있는 "키"라는 명명된 한정자를 첨부자에게 제공하는 첨부 유형입니다.

QML 엔진이 이 코드를 처리하면 첨부된 객체 유형의 단일 인스턴스를 생성하고 이 인스턴스를 Item 객체에 첨부하여 인스턴스의 enabledreturnPressed 속성에 대한 액세스 권한을 제공합니다.

첨부된 객체를 제공하는 메커니즘은 첨부된 객체 유형첨부 유형에 대한 클래스를 제공함으로써 C++에서 구현할 수 있습니다. 첨부된 객체 유형의 경우, 첨부된 객체가 액세스할 수 있는 속성을 정의하는 QObject 파생 클래스를 제공합니다. 첨부 유형의 경우 QObject- 파생 클래스를 제공하세요:

  • 는 다음과 같은 서명을 가진 정적 qmlAttachedProperties()를 구현합니다:
    static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);

    이 메서드는 첨부된 객체 유형의 인스턴스를 반환해야 합니다.

    QML 엔진은 이 메서드를 호출하여 첨부된 객체 유형의 인스턴스를 object 매개변수로 지정된 어태치어에 첨부합니다. 메모리 누수를 방지하기 위해 이 메서드를 구현할 때 반드시 필요한 것은 아니지만 반환된 인스턴스의 부모를 object 로 설정하는 것이 일반적입니다.

    이 메서드는 엔진이 후속 첨부 프로퍼티 액세스를 위해 반환된 인스턴스 포인터를 캐시하므로 각 어태치 객체 인스턴스에 대해 엔진에서 최대 한 번만 호출됩니다. 따라서 어태치이( object )가 삭제될 때까지 어태치먼트 객체는 삭제되지 않을 수 있습니다.

  • 클래스 선언에 QML_ATTACHED(attached) 매크로를 추가하여 첨부 유형으로 선언합니다. 인수는 첨부된 객체 타입의 이름입니다.

첨부된 객체 구현하기: 예제

예를 들어 이전 예제에서 설명한 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 객체에 첨부된 속성으로 구현할 수 있습니다. 앞서 설명한 개념의 관점에서 볼 때 여기에 관련된 당사자는 다음과 같습니다:

  • published 신호와 expired 속성을 제공하는 익명 첨부 개체 유형의 인스턴스. 이 유형은 아래 MessageBoardAttachedType 에 의해 구현됩니다.
  • 어태치어가Message 객체
  • Message 객체가 첨부된 속성에 액세스하는 데 사용하는 첨부 유형이MessageBoard 유형

다음은 구현 예시입니다. 먼저 어태치어가 액세스할 수 있는 필요한 속성과 신호가 있는 어태치먼트 객체 유형이 있어야 합니다:

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() 메서드를 선언해야 합니다. 또한 QML_ATTACHED() 매크로를 통해 MessageBoard 를 첨부 유형으로 선언해야 합니다:

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() 함수를 호출하여 모든 객체에 첨부된 첨부된 객체 인스턴스에 액세스할 수 있습니다.

예를 들어

메시지 *msg = 일부 메시지 인스턴스(); 메시지보드 첨부 유형 *attached = qobject_cast<메시지보드 첨부 유형*>(qmlAttachedPropertiesObject<메시지보드>(msg));
qDebug() << "Value of MessageBoard.expired:" << attached->expired();

첨부된 프로퍼티 전파하기

QQuickAttachedPropertyPropagator 를 서브클래싱하여 fontpalette 전파처럼 부모 객체에서 자식 객체로 첨부 프로퍼티를 전파할 수 있습니다. items , popups, windows 를 통한 전파를 지원합니다.

프로퍼티 수정자 유형

프로퍼티 수정자 유형은 특별한 종류의 QML 객체 유형입니다. 프로퍼티 수정자 유형 인스턴스는 그것이 적용되는 (QML 객체 인스턴스의) 프로퍼티에 영향을 줍니다. 프로퍼티 수정자 유형에는 두 가지 종류가 있습니다:

  • 속성 값 쓰기 인터셉터
  • 속성 값 소스

속성 값 쓰기 인터셉터는 속성에 쓰여지는 값을 필터링하거나 수정하는 데 사용할 수 있습니다. 현재 지원되는 속성 값 쓰기 인터셉터는 QtQuick 가져오기에서 제공하는 Behavior 유형뿐입니다.

속성 값 소스는 시간이 지남에 따라 속성 값을 자동으로 업데이트하는 데 사용할 수 있습니다. 클라이언트는 자체 프로퍼티 값 소스 유형을 정의할 수 있습니다. QtQuick 가져오기에서 제공하는 다양한 프로퍼티 애니메이션 유형은 프로퍼티 값 소스의 예시입니다.

프로퍼티 수정자 유형 인스턴스는 다음 예시와 같이 "<프로퍼티명>에 <수정자 유형>" 구문을 통해 생성하여 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밀리초마다 속성 값을 다른 임의의 숫자로 업데이트합니다. 또한 이 난수 생성기에 최대값을 제공할 수 있습니다. 이 클래스는 다음과 같이 구현할 수 있습니다:

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 객체 유형의 기본 및 상위 속성 지정하기

인스턴스화 가능한 QML 객체 유형으로 등록된 QObject 파생 유형은 선택적으로 해당 유형에 대한 기본 속성을 지정할 수 있습니다. 기본 속성은 개체의 자식이 특정 속성에 할당되지 않은 경우 자동으로 할당되는 속성입니다.

기본 속성은 특정 "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 속성에 명시적으로 할당하지 않고도 항목에 대한 시각적 자식을 선언할 수 있습니다).

또한 "부모 프로퍼티" Q_CLASSINFO()를 선언하여 QML 계층 구조에서 부모 객체를 나타내는 프로퍼티를 QML 엔진에 알릴 수 있습니다. 예를 들어 메시지 유형은 다음과 같이 선언할 수 있습니다:

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 모듈로 시각적 항목 정의하기

모듈로 사용자 인터페이스를 빌드할 때 Qt Quick 모듈로 사용자 인터페이스를 구축할 때 시각적으로 렌더링할 모든 QML 객체는 Item 유형에서 파생되어야 하며, 이는 모든 시각적 객체의 기본 유형이므로 Qt Quick. 이 Item 유형은 QQuickItem C++ 클래스에 의해 구현되며, 이 클래스는 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
    }
};

© 2025 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.