Qt Protobuf QMLタイプ

ジェネレータプラグインを使うと、QMLにprotobufメッセージを登録することができます。型を登録するには、QMLQML_URI の生成キーを使います。APIの詳細はqt_add_protobufコマンドとAPI使用例QML extended protobufを参照してください。

登録された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 で公開するには、protobuf スキーマとqt_add_protobufコマンドにQML を指定します:

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関数はuserdb.proto の protobuf メッセージを QML でサポートしたuserdb_gen というライブラリを生成します。QML でメッセージを使用するには、qt_add_protobufQML_URI 引数で指定された URI を使用して、生成された QML モジュールをインポートしてください:

import userdb.pb

すべてのprotobufメッセージはQMLの値型として登録されています。QMLでこれらを使用するには、QMLの項目にproperty属性を定義します:

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

newUser プロパティのtypenameemail フィールドを変更するには、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 オブジェクトUserDBEngine は、lastAddedUser プロパティと呼び出し可能なメソッド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メッセージの重複宣言を避けるか、あるいは賢明に行 うべきです。異なるprotobufパッケージの中で宣言された同じprotobufメッセー ジ名を複数使用すると、自動生成されるコードの中で矛盾が生じる可能性があります。以下の例では、qtprotobufnamespaceqtprotobufnamespace1.nested という 2 つの異なる proto パッケージが、同じ proto メッセージ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>を使用します(モジュール(名前空間)のインポートを参照)。以下に、protobufパッケージを異なる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やJavaScriptでは予約されているが、*.protoでは予約されていないキーワードに注意してください。QMLで予約されている名前のフィールドは、ジェネレータプラグインによって_proto というサフィックスによって無言で拡張されます。例えば、idpropertyimport は予約されたキーワードです。これらは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)

また、enum フィールドはアンダースコア記号で始めることはできません。このようなフィールドはそのまま生成されますが、将来QMLエンジンがこのようなフィールドの登録を許可しない限り、QMLの中では未定義となります。以下の*.proto の例を参照してください:

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

生成された出力:

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

QML プロパティの構文についての詳細は、Defining Property Attributes を参照してください。

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。