With the
generator plugin
, you can register protobuf messages in the QML. To register the type, use the
QML
and
QML_URI
generation keys. See API details in
qt_add_protobuf
command and API usage example
QML extended protobuf
.
Registered protobuf messages are available in the QML, like built-in Q_GADGET types. The registration is done via the QML module.
使用 generator plugin to generate libraries of protobuf messages that you can access from Qt Quick applications. The Qt Protobuf CMake API has respective options that control the QML module creation.
For example, you have the
userdb.proto
protobuf schema that contains the
User
message:
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; }
To expose the
User
message in QML, use the protobuf schema and the
qt_add_protobuf
command with the
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)
The
qt_add_protobuf
function will generate a library called
userdb_gen
, containing the protobuf messages from
userdb.proto
with QML support. To use the messages in QML, import the generated QML module using the URI specified in the
QML_URI
argument of the
qt_add_protobuf
call:
import userdb.pb
All protobuf messages are registered as QML 值类型 . To use them in QML, define the property attribute for some QML item:
Window { id: userAddForm property user newUser ... }
To change the
type
,
名称
,或
email
fields of the
newUser
property, use QML signal callbacks. For example:
TextField { id: userNameField onTextChanged: { userAddForm.newUser.name = userNameField.text } } ... TextField { id: userEmailField onTextChanged: { userAddForm.newUser.email = userEmailField.text } }
The
User.Type
enum values are also accessible from QML. The below example shows how to create a ComboBox item using the enum values:
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 } }
C++ classes registered in QML may use the messages created in QML in both properties and invocable methods.
The singleton QML object
UserDBEngine
exposes the
lastAddedUser
property and the invocable method
addUser
to 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); ... }
The
lastAddedUser
property has the
userdb::User
type generated from the
userdb.proto
schema from the previous section. The invocable
addUser
method accepts constant reference to an object of
userdb::User
type. Both property and method can be used from 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 }
You should avoid declaration of protobuf message duplicates in your
*.proto
files or do it wisely. If your application uses several identical protobuf message names declared inside different protobuf packages, they might contradict each other in auto-generated code. In the example below, two different proto packages,
qtprotobufnamespace
and
qtprotobufnamespace1.nested
, use the same proto message
NestedFieldMessage
. The file
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; }
In case there is no possibility to avoid name duplicates among packages, then put duplicated messages in different QML modules and use a <Qualifier> for each QML module import, see 模块 (名称空间) 导入 . Below the example how to add protobuf packages into different QML modules:
# 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" )
The <Qualifier> usage example:
import qtprotobufnamespace as NestedFieldMessages import qtprotobufnamespace1.nested as FieldMessages_Nested1 ... property NestedFieldMessages.nestedFieldMessage fieldMsg1; property FieldMessages_Nested1.nestedFieldMessage fieldMsg2;
注意: The usage of duplicates will trigger a warning at compilation time.
Pay attention to the keywords that are reserved in QML or JavaScript context, but not reserved in *.proto context. Fields with names that are reserved by QML will be silently extended by
_proto
suffix by the
generator plugin
。例如,
id
,
property
,和
import
are reserved keywords. They will be replaced by
id_proto
,
property_proto
,
import_proto
:
message MessageUpperCaseReserved { sint32 Import = 1; sint32 Property = 2; sint32 Id = 3; }
Generated code output:
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 ...)
Also, enum values cannot begin with a lower case letter. The
generator plugin
will capitalize the first letter in code output. See the
*.proto
example below:
enum LowerCaseEnum { enumValue0 = 0; enumValue1 = 1; enumValue2 = 2; }
Generated code output:
enum LowerCaseEnum { EnumValue0 = 0, EnumValue1 = 1, EnumValue2 = 2, }; Q_ENUM(LowerCaseEnum)
Also, enum fields cannot begin with an underscore symbol. Such fields will be generated as is, but will be undefined in the QML, unless the QML engine will allow registering them in the future. See the
*.proto
example below:
enum UnderScoreEnum { _enumUnderscoreValue0 = 0; _EnumUnderscoreValue1 = 1; }
Generated output:
enum UnderScoreEnum { _enumUnderscoreValue0 = 0, _EnumUnderscoreValue1 = 1, }; Q_ENUM(UnderScoreEnum)
For more information about the QML properties syntax, check Defining Property Attributes .