Qt Protobuf QML 유형

생성기 플러그인을 사용하면 QML에 프로토타입 메시지를 등록할 수 있습니다. 유형을 등록하려면 QMLQML_URI 생성 키를 사용합니다. API 세부 정보는 qt_add_protobuf 명령과 API 사용 예제 QML 확장 protobuf를 참조하세요.

등록된 프로토뷰 메시지는 기본 제공 Q_GADGET 유형과 같이 QML에서 사용할 수 있습니다. 등록은 QML 모듈을 통해 이루어집니다.

QML에서 protobuf 메시지 사용하기

생성기 플러그인을 사용하여 Qt Quick 애플리케이션에서 액세스할 수 있는 protobuf 메시지 라이브러리를 생성하세요. Qt Protobuf CMake API에는 QML 모듈 생성을 제어하는 각각의 옵션이 있습니다.

예를 들어 User 메시지가 포함된 userdb.proto protobuf 스키마가 있습니다:

syntax = "proto3";

package userdb;

message User {
    enum Type {
        Admin = 0;
        Manager = 1;
        Account = 2;
        Director = 3;
    }
    Type type = 1;
    string name = 2;
    string email = 3;
}

User 메시지를 QML에 노출하려면 QML 인수와 함께 protobuf 스키마와 qt_add_protobuf 명령을 사용합니다:

qt_add_executable(appuserdb
    ...
)

qt_add_qml_module(appuserdb
    URI userdb
    VERSION 1.0
    QML_FILES
        ...
    SOURCES
        ...
)

qt_add_protobuf(userdb_gen
    QML
    QML_URI "userdb.pb"
    PROTO_FILES
        userdb.proto
)

target_link_libraries(appuserdb PRIVATE userdb_gen)

qt_add_protobuf 함수는 QML을 지원하는 userdb.proto 의 protobuf 메시지를 포함하는 userdb_gen 라는 라이브러리를 생성합니다. QML에서 메시지를 사용하려면 qt_add_protobuf 호출의 QML_URI 인수에 지정된 URI를 사용하여 생성된 QML 모듈을 가져옵니다:

import userdb.pb

모든 protobuf 메시지는 QML 값 유형으로 등록됩니다. QML에서 사용하려면 일부 QML 항목에 대한 속성 속성을 정의하세요:

Window {
    id: userAddForm
    property user newUser
    ...
}

newUser 속성의 type, name, email 필드를 변경하려면 QML 신호 콜백을 사용합니다. 예를 들어

TextField {
    id: userNameField
    onTextChanged: {
        userAddForm.newUser.name = userNameField.text
    }
}
...
TextField {
    id: userEmailField
    onTextChanged: {
        userAddForm.newUser.email = userEmailField.text
    }
}

User.Type 열거형 값도 QML에서 액세스할 수 있습니다. 아래 예시는 열거형 값을 사용하여 ComboBox 항목을 만드는 방법을 보여줍니다:

ComboBox {
    id: userTypeField
    textRole: "key"
    model: ListModel {
        id: userTypeModel
        ListElement { key: "Admin"; value: User.Admin }
        ListElement { key: "Second"; value: User.Manager }
        ListElement { key: "Account"; value: User.Account }
        ListElement { key: "Director"; value: User.Director }
    }
    onActivated: function(index) {
        userAddForm.newUser.type = userTypeModel.get(index).value
    }
}

QML과 C++ 통합

QML에 등록된 C++ 클래스는 프로퍼티와 호출 가능한 메서드 모두에서 QML에서 생성된 메시지를 사용할 수 있습니다.

싱글톤 QML 객체 UserDBEnginelastAddedUser 프로퍼티와 호출 가능한 메서드 addUser 를 QML에 노출합니다:

class UserDBEngine : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    QML_SINGLETON

    Q_PROPERTY(userdb::User lastAddedUser READ lastAddedUser WRITE setLastAddedUser NOTIFY lastAddedUserChanged FINAL)
public:
    ...
    Q_INVOKABLE void addUser(const userdb::User &newUser);
    ...
}

lastAddedUser 속성에는 이전 섹션의 userdb.proto 스키마에서 생성된 userdb::User 유형이 있습니다. 호출할 수 없는 addUser 메서드는 userdb::User 유형의 객체에 대한 상수 참조를 허용합니다. 프로퍼티와 메서드 모두 QML에서 사용할 수 있습니다:

Button {
    text: "Add"
    onClicked: {
        // Use the property created in the previous section
        UserDBEngine.addUser(userAddForm.newUser)
    }
}
...
Text {
    // The text will be updated automatically when lastAddedUser is changed
    text: "Last added user: " + UserDBEngine.lastAddedUser.name
}

Protobuf 메시지 중복

*.proto 파일에서 중복된 protobuf 메시지 선언을 피하거나 현명하게 처리해야 합니다. 애플리케이션에서 서로 다른 프로토뷰 패키지 안에 선언된 여러 개의 동일한 프로토뷰 메시지 이름을 사용하는 경우 자동 생성된 코드에서 서로 모순될 수 있습니다. 아래 예시에서는 qtprotobufnamespaceqtprotobufnamespace1.nested 라는 두 개의 서로 다른 프로토 패키지가 동일한 프로토 메시지 NestedFieldMessage 를 사용합니다. 파일 nested.proto:

syntax = "proto3";

package qtprotobufnamespace;
import "externalpackage.proto";

message NestedFieldMessage {
    sint32 testFieldInt = 1;
}

파일 nestedspace1.proto:

syntax = "proto3";

package qtprotobufnamespace1.nested;

message NestedFieldMessage {
    message NestedMessage {
        sint32 field = 1;
    }
    NestedMessage nested = 1;
}

패키지 간에 이름 중복을 피할 수 없는 경우에는 중복된 메시지를 다른 QML 모듈에 넣고 각 QML 모듈 가져오기에 <Qualifier>를 사용하세요( 모듈(네임스페이스) 가져오기를 참조하세요). 아래 예제에서는 프로토버프 패키지를 다른 QML 모듈에 추가하는 방법을 설명합니다:

# qtprotobufnamespace QML module
qt_add_protobuf(nestedtypes_qtprotobuf_qml
    PROTO_FILES
        nested.proto
    QML
    QML_URI
        qtprotobufnamespace
    OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/qt_protobuf_gen1"
)

...

# qtprotobufnamespace1.nested QML module
qt_add_protobuf(nestedspace_qml
    PROTO_FILES
        nestedspace1.proto
    QML
    QML_URI
        qtprotobufnamespace1.nested
    OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/qt_protobuf_gen2"
)

<Qualifier> 사용 예제:

import qtprotobufnamespace as NestedFieldMessages
import qtprotobufnamespace1.nested as FieldMessages_Nested1

...

property NestedFieldMessages.nestedFieldMessage fieldMsg1;
property FieldMessages_Nested1.nestedFieldMessage fieldMsg2;

참고: 중복을 사용하면 컴파일 시 경고가 트리거됩니다.

QML 유형 중복

애플리케이션에서 QML에 이미 예약된 이름을 가진 protobuf 메시지를 QML 유형으로 사용하는 경우 올바른 동작을 보장할 수 없으며 Element is not creatable 오류가 트리거됩니다. QML 유형의 중복을 방지하려면 모듈(네임스페이스) 가져오기를 참조하여 QML 모듈 가져오기에 <Qualifier>를 사용하세요. 예를 들어, 글로벌 네임스페이스로 가져올 때 다음 protobuf 메시지는 TextItem QML 유형과 충돌할 수 있습니다:

syntax = "proto3";
package test.example;

message Text {
    string text = 1;
}

message Item {
    sint32 width = 1;
    sint32 height = 2;
}

위의 프로토뷰 메시지에서 QML 유형을 생성하려면 QML 옵션과 함께 qt_add_protobuf 매크로를 사용하세요. 아래 예시를 참조하세요:

qt_add_protobuf(example
    PROTO_FILES
        test.proto
    QML
    QML_URI
        test.example
)

QtQuick 가져오기 이후에 프로토뷰 메시지를 전역 네임스페이스로 가져오면 QML 유형과의 충돌이 트리거됩니다. 아래 예시를 참조하세요:

 import QtQuick
 import test.example

 Item {
    id: root
    ...
    property ProtobufMessages.item itemData
}

QML 유형과의 충돌을 방지하려면 <Qualifier> 을 사용하여 생성된 QML 모듈을 로컬 네임스페이스로 가져오세요. 아래 예시를 참조하세요:

// No qualifier - global namespace
import QtQuick
// ProtobufMessages - a qualifier of local namespace.
import test.example as ProtobufMessages

Item {
    id: root
    ...
    property ProtobufMessages.item itemData
}

QML 키워드 처리

QML 또는 JavaScript 컨텍스트에서는 예약되어 있지만 *.proto 컨텍스트에서는 예약되어 있지 않은 키워드에 주의하세요. QML에 의해 예약된 이름을 가진 필드는 생성기 플러그인에 의해 _proto 접미사로 자동 확장됩니다. 예를 들어 id, property, import 는 예약된 키워드입니다. id_proto , property_proto, import_proto 로 대체됩니다:

message MessageUpperCaseReserved {
    sint32 Import = 1;
    sint32 Property = 2;
    sint32 Id = 3;
}

생성된 코드 출력:

Q_PROPERTY(QtProtobuf::sint32 import_proto READ import_proto ...)
Q_PROPERTY(QtProtobuf::sint32 property_proto READ property_proto ...)
Q_PROPERTY(QtProtobuf::sint32 id_proto READ id_proto ...)

또한 열거형 값은 소문자로 시작할 수 없습니다. 생성기 플러그인은 코드 출력의 첫 글자를 대문자로 표시합니다. 아래 *.proto 예시를 참조하세요:

enum LowerCaseEnum {
    enumValue0 = 0;
    enumValue1 = 1;
    enumValue2 = 2;
}

생성된 코드 출력:

enum LowerCaseEnum {
    EnumValue0 = 0,
    EnumValue1 = 1,
    EnumValue2 = 2,
};
Q_ENUM(LowerCaseEnum)

또한 열거형 필드는 밑줄 기호로 시작할 수 없습니다. 이러한 필드는 그대로 생성되지만 향후 QML 엔진에서 등록을 허용하지 않는 한 QML에서 정의되지 않습니다. 아래 *.proto 예시를 참조하세요:

enum UnderScoreEnum {
    _enumUnderscoreValue0 = 0;
    _EnumUnderscoreValue1 = 1;
}

생성된 출력:

enum UnderScoreEnum {
    _enumUnderscoreValue0 = 0,
    _EnumUnderscoreValue1 = 1,
};
Q_ENUM(UnderScoreEnum)

QML 속성 구문에 대한 자세한 내용은 속성 속성 정의하기를 참조하세요.

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