Skip to content

nsuke/protobuf-qml

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

protobuf-qml

Build Status Build status

Efficient schematized serialization and RPC for QtQuick2 applications through Protocol Buffers and gRPC bindings.

Usage

For installation, see INSTALL.md.

Protocol Buffers binding

For Protocol Buffers itself, see upstream official site:

https://developers.google.com/protocol-buffers/

Code generation

Suppose you write following .proto file, say my_foo.proto:

    message Foo {
      string text = 1;
      repeated uint32 nums = 2;
    }

you can generate QML/Javascript message type using compiler plugin:

$ protoc --qml_out gen my_foo.proto

This will yield gen/my_foo.pb.js file. Let's import this file from any QML using relative path.

Docker image

If you want to try it out without building protobuf-qml, a docker image for code generator is available.

# docker pull nsuke/protobuf-qml
# docker run -it -v $(pwd):/opt/protobuf-qml nsuke/protobuf-qml -I. \
    --qml_out=<relative path to output dir> \
    <relative path to your .proto file>

Note that the relative paths cannot contain parent or sibling directories, i.e., they need to be within current or sub directories.

Serialization

import 'gen/my_foo.pb.js' as Types

then you can use the message type inside signal handlers, property bindings or functions:

var foo = new Types.Foo({
  text: 'hello world',
  nums: [ 3, 4, 5 ],
});

var buf = foo.serialize();

here, the buf variable is an ArrayBuffer object.

Deserialization

Deserialization is quite simple too:

var foo2 = Types.Foo.parse(buf);

console.log(foo2.text)
// output: hello world
console.log(foo2.nums(1))
// output: 4

TBD: Link to full sample code

gRPC binding

For gRPC itself, see upstream official page: http://www.grpc.io/

gRPC binding is still experimental.

Code generation

Suppose you add service definition to the my_foo.proto above:

service MyService {
  rpc ExchangeFoo(Foo) returns(Foo) {}
}

compiler plugin will additionally yield MyService.qml and MyServiceClient.qml files besides my_foo.pb.js file. Let's import the directory containing those QML files:

import 'gen'

then you can instantiate QML elements:

MyServiceClient {
  id: client
}
MyService {
  id: service
}

Client

To make the client element functional, plug it to a gRPC channel. (In fact, you can plug to custom RPC implementation but gRPC works out of the box)

import 'gen'
import gRPC
GrpcChannel {
  id: gchannel
  target: 'example.org:44448'
  credentials: InsecureCredentials {}
}

MyServiceClienit {
  id: client
  channel: gchannel
}

Then inside signal handlers, property bindings or functions :

client.exchangeFoo({
  text: 'hi',
  nums: [1, 2, 3],
}, function(err, responseFoo) {
  if (err) {
    console.warn('Received error response: ' + err);
    return;
  }

  // Do some useful stuff with "responseFoo" content
});

This will make a RPC call to example.org port 44448.

Server

You can create gRPC server in QML app if you want to. It's handy for P2P communication and/or notifications, but not suitable for heavy computation.

import 'gen'
import gRPC
GrpcServer {
  id: gserver
  address: '0.0.0.0:44448'
  credentials: ServerInsecureCredentials {}
}

MyService {
  id: server
  server: gserver

  // This function is called for each incoming request.
  exchangeFoo: function(requestFoo, callback) {

    // First argument is error.
    callback(null, {
      text: 'In response to ' + requestFoo.text,
      nums: [42],
    });
  }
}

QtWebSockets with Qt 5.8+

From Qt 5.8 (QtWebSockets 1.1 QML module) on, we can send array buffers through websockets.

Sending serialized message with QtWebSockets

import QtWebSockets 1.1
WebSocket {
  id: socket
  url: 'ws://your.remote.server'
  active: true
}
// Inside function or handler
var msg = new Types.Foo({
  text: 'hello world',
  nums: [ 3, 4, 5 ],
});
var buf = msg.serialize();
socket.sendBinaryMessage(buf);

Receiving serialized message with QtWebSockets

WebSocket {
  id: socket
  url: 'ws://your.remote.server'
  active: true

  onBinaryMessageReceived: {
    var msg = Types.Foo.parse(message);
    console.log(msg.text);
    console.log(msg.nums(0));
    console.log(msg.nums(1));
    // ... Do whatever with the data
  }
}

The complete code is available under examples/WebSockets directory.

Future examples

In the future, it might become possible to send ArrayBuffer using XMLHttpRequest too.

I've submitted a patch for XMLHttpRequest to Qt project which enables following usage.

Send and receive serialized message with XMLHttpRequest

// Inside function or handler
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://your.remote.server/');
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    var response = Types.Foo.parse(xhr.response);
    // ... Do whatever with the data
  }
};
xhr.responseType = 'arraybuffer';

var msg = new Types.Foo({
  text: 'hello world',
  // ...,
});

var buf = msg.serialize();

xhr.send(new DataView(buf));

The complete code is available under examples/XMLHttpRequest directory.