To communicate with a QWebChannel or WebChannel, a client must use and set up the JavaScript API provided by qwebchannel.js
.
For clients run inside Qt WebEngine, you can load the file via qrc:///qtwebchannel/qwebchannel.js
. For external clients, you need to copy the file to your web server. Then
instantiate a QWebChannel object and pass it a transport object and a callback function, which will be invoked once the initialization of the channel finishes and the published objects become
available.
The transport object implements a minimal message passing interface. It should be an object with a send()
function, which takes a stringified JSON message and transmits it to the server-side QWebChannelAbstractTransport object. Furthermore, its onmessage
property should be called when a message from the server was received. Alternatively, you can use a
WebSocket to implement the interface.
Note that the JavaScript QWebChannel object should be constructed once the transport object is fully operational. In case of a WebSocket, that means you should create the QWebChannel in the socket's onopen
handler. Take a look at the Qt WebChannel Standalone Example to see how this is done.
Once the callback passed to the QWebChannel object is invoked, the channel has finished initialization and all published objects are accessible to the HTML client via the
channel.objects
property. Thus, assuming an object was published with the identifier "foo", then we can interact with it as shown in the example below. Note that all communication between the HTML client and the
QML/C++ server is asynchronous. Properties are cached on the HTML side. Furthermore keep in mind that only QML/C++ data types which can be converted to JSON will be (de-)serialized properly and thus accessible to HTML
clients.
new QWebChannel(yourTransport, function(channel) { // Connect to a signal: channel.objects.foo.mySignal.connect(function() { // This callback will be invoked whenever the signal is emitted on the C++/QML side. console.log(arguments); }); // To make the object known globally, assign it to the window object, i.e.: window.foo = channel.objects.foo; // Invoke a method: foo.myMethod(arg1, arg2, function(returnValue) { // This callback will be invoked when myMethod has a return value. Keep in mind that // the communication is asynchronous, hence the need for this callback. console.log(returnValue); }); // Read a property value, which is cached on the client side: console.log(foo.myProperty); // Writing a property will instantly update the client side cache. // The remote end will be notified about the change asynchronously foo.myProperty = "Hello World!"; // To get notified about remote property changes, // simply connect to the corresponding notify signal: foo.myPropertyChanged.connect(function() { console.log(foo.myProperty); }); // One can also access enums that are marked with Q_ENUM: console.log(foo.MyEnum.MyEnumerator); });
When you publish a QObject
that has overloaded methods, QWebChannel will resolve method invocations to the best match. Note that due to JavaScript's type system, there is only
a single 'number' type which maps best to a C++ 'double'. When overloads differ only in the type of a number-like parameter, QWebChannel will always choose that overload which best matches the
JavaScript 'number' type. When you connect to an overloaded signal, the QWebChannel client will by default only connect to the first signal overload of that name. Additionally, overloads of
methods and signals can explicitly be requested by their complete QMetaMethod
signature. Assume we have the following QObject
subclass on the C++ side:
class Foo : public QObject { Q_OBJECT slots: void foo(int i); void foo(double d); void foo(const QString &str); void foo(const QString &str, int i); signals: void bar(int i); void bar(const QString &str); void bar(const QString &str, int i); };
Then you can interact with this class on the JavaScript side like this:
// methods foo.foo(42); // will call the method named foo which best matches the JavaScript number parameter, i.e. foo(double d) foo.foo("asdf"); // will call foo(const QString &str) foo.foo("asdf", 42); // will call foo(const QString &str, int i) foo["foo(int)"](42); // explicitly call foo(int i), *not* foo(double d) foo["foo(QString)"]("asdf"); // explicitly call foo(const QString &str) foo["foo(QString,int)"]("asdf", 42); // explicitly call foo(const QString &str, int i) // signals foo.bar.connect(...); // connect to first signal named bar, i.e. bar(int i) foo["bar(int)"].connect(...); // connect explicitly to bar(int i) foo["bar(QString)"].connect(...); // connect explicitly to bar(const QString &str) foo["bar(QString,int)"].connect(...); // connect explicitly to bar(const QString &str, int i)