Qt WebChannel – bridging the gap between C++/QML and the web
Hybrid applications, which mix a UI built with Qt Widgets or Qt Quick with embedded HTML websites, are very popular. In particular, something like an HTML 5 app framework is often requested by customers. Qt WebKit makes it trivial to embed HTML content in any graphical application. But how does one bridge the gap between C++/QML on one side, and HTML/JavaScript on the other side?
A bit of history
In the single-process world of the C++ Qt WebKit API, it was easily possible to publish a QObject instance to the JavaScript client side, thanks the Qt WebKit Bridge. This is a crucial functionality for any application that uses Qt WebKit as the foundation for an HTML app framework or similar.
Up until now though, WebView, the Qt Quick 2 integration of Qt WebKit, was missing this functionality. The reason for that was the multi-process architecture at the core of WebKit2, which is used internally by WebView; in such an environment, the synchronous API of the WebKit bridge cannot be supported, since inter-process communication is inherently asynchronous. Due to that, hybrid QML/HTML applications were notoriously hard to implement.
In October 2011, Noam Rosenthal presented an alternative approach to the problem, which he called Qt WebChannel. His idea was simple and powerful at the same time: By leveraging Qt’s introspection system, he could create mock objects on the JavaScript client side which mirror the API of the server-side QML/QObject objects. For communication between the QML host and the HTML/JavaScript client, he chose WebSockets. But, contrary to the WebKit Bridge, the API provided by the WebChannel is completely asynchronous.
Despite the huge interest in hybrid QML/HTML applications, Nokia and nowadays, Digia, did not prioritize work on this missing functionality. Priorities lay elsewhere and the Qt WebChannel project never left the proof-of-concept stage on Qt Labs.
At the beginning of 2013, a customer approached KDAB and requested the development of a QML application with an HTML 5 application framework. Naturally, we chose Qt WebChannel as a foundation and started polishing it. In this first real-world use-case, numerous bugs and missing functionality were found, fixed and upstreamed to the Qt Labs repository. In the process, I essentially took up maintainership of the Qt WebChannel project. Eventually, the codebase worked reliably and efficiently even on embedded devices. Still, it remained a Qt Labs project and as such awkward to use by others.
Making it Official
Over the last couple of months, I had the pleasure to work on Qt WebChannel exclusively, sponsored by KDAB. The goal was to create a proper Qt module which can be used by hybrid QML/HTML applications. At this year’s Qt Contributor Summit the design was reviewed and approved for inclusion in Qt 5.4. A few days ago now, Qt WebChannel was added as a new module for Qt 5.4. What is left now, is the inclusion of two already approved patches, which provide an easy-to-use integration of the Qt WebChannel functionality into Qt WebKit with a minimum of boiler-plate code.
A QML/HTML hybrid application in Qt 5.4
So, what is necessary to build a hybrid QML/HTML application in the upcoming Qt 5.4? The following shows how to use the new Qt WebChannel module.
QML Server Side
On the QML server side, first import the Qt WebChannel module, as well as the Qt WebKit module and its experimental one:
import QtWebChannel 1.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0
Now, let’s create an object that we want to publish to the HTML/JavaScript clients:
QtObject { id: myObject // the identifier under which this object // will be known on the JavaScript side WebChannel.id: "foo" // signals, methods and properties are // accessible to JavaScript code signal someSignal(string message); function someMethod(message) { console.log(message); someSignal(message); return "foobar"; } property string hello: "world" }
Publishing the object to the HTML clients in your WebView is as simple as
WebView { experimental.webChannel.registeredObjects: [myObject] }
This single line is all you need to publish potentially multiple objects. Internally, this will use the WebKit IPC mechanism to transmit method calls, signals and property update notifications to the HTML clients. You can also create a WebChannel object externally and set it on multiple WebViews if necessary.
HTML/JavaScript client side
On the client side, a bit of boiler-plate code is still required. I tried to minimize it as much as possible, and plan to improve this situation even further in the future. First, include the client-side qwebchannel.js
library via its Qt resource URL:
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
Then, in your JavaScript code, instantiate a QWebChannel
object and pass it a callback function. The callback function gets invoked when the initialization of the web channel succeeded. Furthermore, you can pass the navigator.qtWebChannelTransport
object to your channel – more on this below.
new QWebChannel(navigator.qtWebChannelTransport, function(channel) { // all published objects are available in channel.objects under // the identifier set in their attached WebChannel.id property var foo = channel.objects.foo; // access a property alert(foo.hello); // connect to a signal foo.someSignal.connect(function(message) { alert("Got signal: " + message); }); // invoke a method, and receive the return value asynchronously foo.someMethod("bar", function(ret) { alert("Got return value: " + ret); }); });
That’s it. I hope you like this new functionality. While it was written with the WebView integration in mind, this new design proved to be much more versatile when compared to the old WebKit Bridge, as it allows you to publish QObject instances to any JavaScript client you can talk to, e.g. via WebSockets. This works in any modern desktop or mobile browser, and no Qt is required on the client side at all. Our friends from Basyskom e.g. wrote a node.js example. Also note that QML is not required at all, you can also create a QWebChannel
and publish QObject
instances in pure Qt/C++ applications. I will write more on these topics in a future blog post.
Is it possible to run a qml client app in web browser? And connect to the server side through websocket?
Yes, that is theoretically possible. Take a look at QML-Web by my colleague Anton Kreuzkamp. There’s also the talk about QML Web by him and Thomas McGuire from last years DevDays Europe. It should be fairly trivial to pair that with the Qt WebChannel to seamlessly write QML web applications with a C++ or QML server-side.
QmlWeb would be the perfect complement for Qt WebChannel!
As for the server/websocket connection, this is also a great solution for situations where the JS code would be just too much for the browser to parse. For example in a communication web client…
Nice work Milian!
Is there a C++ variant of qwebchannel.js ?
I mean can I use WebChannel with a web socket transport in a QML/C++ frontend to communicate with a remote QML/C++ backend as well as from HTML/JavaScript ?
No, qwebchannel.js is only available as a JavaScript library. If you want to use it in C++, you’ll need to do more to recreate the objects in a typesafe manner on the client side. Watch out for Qt Replicant, which was announced on the development mailing list of Qt and demoed at this years Qt Contributor Summit. We will definitely blog about it, once the code is made public.
That said, you can import qwebchannel.js in QML and use it to communicate with a C++ or QML backend. Take a look at the WebChannel tests which do something like that. For communication purposes, you’ll need to create a WebSocket-based transport layer, which you can copy from the WebChannel examples (e.g. standalone).
Enjoy!
Great! So, we can make a middleware with QML&C++ now. Q2EE? 🙂
Awesome work Milian!
Glad to see this idea from 3 years ago survived somehow…
Hey Noam,
I’m glad to see you are still around! Thanks a lot for spearheading this effort back then, your initial idea was spot on. Much appreciated!
so.. what about using it with QtWebEngine ?
It will be just as easy as Qt WebKit with Qt 5.5. In Qt 5.4, you’ll have to do some manual work and use a WebSocket for communication. Or apply this locally: https://codereview.qt-project.org/#/c/93800/
Hi Millian
With my understanding, webchannel is working in asychnchronized way. in our case, there are a few number of custom properties and methods must be accessed synchronously. Any idea to use Webchannel in synchronized way?
BR
Glen
Hey Glen,
please read the article again. There is no way to make the API synchronous, as we must do inter-process communication, which is inherently asynchronous.
I suggest you refactor your code. There is normally always a way to do so, in order to work properly with asynchronous API.
Bye
Hi — In 5.4 can you describe how the process might work with the QML WebSocket Server as the WebChannel transport (rather than the ipc approach which looks like it won’t be available until 5.5)
The modification on the javascript client side seems simple enough (use a websocket with the QML servers url rather than the navigator.qtWebChannel)
but on the QML server side I’m a little uncertain… basically I know you need to create a webSocket server, and then create a WebChannel with the websocket server as the transport, but I haven’t been able to find any concrete instructions on how this is done in QML (I think the only thing I found was on the c++ side)
Thanks for the cool article!
if maybe you could look into my question .plz .
thx .
http://stackoverflow.com/questions/32396649/how-to-invoke-c-qt-function-from-javascript-in-qwebengine?answertab=votes#tab-top
Done, use
qt.webChannelTransport.
in QtWebEngine.Tnx a million.
For later readers, Note for using this we have to import import QtWebEngine 1.1
I was looking for exactly the same information while porting from Qml1+WebView (1.0) + javascriptWindowObjects to Qml2+WebEngine+WebChannel.
Could not find it in the 5.5 documentation, so thanks a lot for this answer!
There really should be a full WebEngine+WebChannel Qml example in the docs, as it’s the only real replacement for the full Qml1+WebView +javascriptWindowObjects integration.
Hi Milian, you have answered a question about QtWebEngine that “It will be just as easy as Qt WebKit with Qt 5.5. In Qt 5.4, you’ll have to do some manual work and use a WebSocket for communication. ”
Now in the 5.5 I still do not see a way. I use qt.webChannelTransport. in the HTML side. But how should I connect to that in the c++/qml side ? Is it by setting transports property of WebChannel qml object ? If so, what should it be ? Thanks
On the C++ side you can access the channel via the page:
http://doc.qt.io/qt-5/qwebenginepage.html#webChannel
On the QML side the WebEngineView has a webChannel property, i.e.:
For some reason, the documentation of this property is missing, I’ll investigate and improve that.
Cheers
The documentation should get updated soon, the current snapshot includes the documentation properly:
http://doc-snapshots.qt.io/qt5-5.6/qml-qtwebengine-webengineview.html#webChannel-prop
Hi, thank you so much for sharing all this information. I have a problem, using WebView like your example works perfect, recently I update Qt and I really like how WebEngineView works but I can’t make that bridge between QML and HTML.
I’m using webChannel.registeredObjects:[myObject], like in your example but nothing seems to work. I don’t know if I’m making things wrong. Thanks again, I hope you can help me.
Never mind! I hadn’t understood the question of corey.
qt.webChannelTransport
This do the trick, thank you so much, sorry that I didn’t get it.
Hello, what can I pass custom data into JS from C++ though QWebChannel?
What problems exactly do you have? But I don’t think the comment section of a blog post is the right place to get support. Please use the official Qt support channels for that, e.g. the mailing list.
Hi! I’ve seen the WebChannel examples (http://doc.qt.io/qt-5/qtwebchannel-examples.html) and would like to avoid WebSockets if possible. How to use QWebChannel without WebSockets correctly?
Just use the webchannel that is bundled with QtWebKit’s WebView or the analogous WebEngineView. See e.g.: http://doc-snapshots.qt.io/qt5-5.6/qml-qtwebengine-webengineview.html#webChannel-prop
thank you so much!
Hi Milian,
I’m trying to use your example but I’m not able to get working. Is possible to have a repro working to look at?
Hey Daniele,
what exactly is the problem? The Qt WebChannel repository contains examples in compilable form. Additionally, both Qt WebKit and WebEngine contain examples that show the direct integration with them.
Please refer to these examples to see how this all works out.
Hi,
I am loading main.qml in my C++/Qt main file (main.cpp) using QQmlApplicationEngine.
I am using WebEngineView (inside my main.qml file) and i load different HTML/JS pages e.g. onButtonClick.
I use webchannel property of WebEngineView and register different QML objects that i need to access from HTML/JS.
I want to expose some objects’ properties and methods of my C++/Qt objects via QWebChannel to HTML/JS.
How can i go about it. Is it possible to use WebChannel of WebEngineView (main.qml) and register my C++/Qt objects from my main.cpp file. Or should a separate QWebChannel be defined in C++/Qt.
Need help.
Thanks.
Make the C++/Qt objects accessible to QML (context property or any of the other options) and then register them on the WebChannel from the WebEngineView – no need for a separate QWebChannel in C++.
Cheers
Hi Millian,
May be the question wont be relevant in this post, but whenever i try to open a qml file remotely on a qtcreator designer, qtcreator crashes. This behavior is sometimes observed on program that use qml but not always otherwise when working on local system it runs smoothly
Thanks
I don’t see how this has anything to do with Qt WebChannel. Furthermore, always report bugs in bug trackers, not in blog post comments. See: https://bugreports.qt.io