Friday, 11 September 2015

QT WebEngineView Communication with Javascript

Introduction

The QT WebEngineView is the new method of providing applications that display, and interact with web pages.

There is some documentation on the QT website on how to implement these functions, with references to the previous QWebView method, but these are lacking examples.

I have produced the following with QT5.5:

Download C++ QT Source for an example, which is a simple QT application with a dialog containing a widget (called HtmlPage).  This widget is based on QWebEngineView, and extends it very slightly to:

  • Initialise the web page, and set up a communications channel to it
  • Provide a function to request the JavaScript web page to  insert a dot at a given X/Y coordinate.
  • Provide a function to receive information regarding a cursor movement and emit a signal to the main window.

Similarly, the example web page has embedded JavaScript to:
  • Initialise and set up a communications channel to the QT application
  • Produce a  dot at a given X/Y coordinate.
  • Emit a signal (function call) to the QT application indicating the cursor has moved.
  • Include a signal handler to place a large dot at the mouse cursor position, and emit a signal when the mouse is clicked.

So the example has two-way communication with the JavaScript inside the web page.
  • When the mouse is clicked (Javascript), the handler places a large dot at the cursor position.
  • The Javascript then informs the QT C++ application with a £widget.functioncall£.
  • Some time later (asynchronously), the C++ application receives the message in the "functioncall" slot.
  • The C++ class emits a signal to the mainwindow to allow the X/Y coordinates to be updated on the screen.
  • The C++ class then makes a call back to the Javascript to place a smaller dot at the same position.
  • Some time later (asynchronously), the Javascript handler receives the message and places the dot.

Hopefully, the code will be somewhat explanatory, but here's a quick overview of the important bits:

C++

Add the "QT += webenginewidgets webchannel" to your project.pro file, and include the appropriate header files, and then in your C++ class / you need to set up the  communications channel.  Note that 'channel' should be declared in the class header.
// Set up the communications channel for this QWebEngineView parented class
this->page()->setWebChannel(&channel) ;
channel.registerObject("widget", this) ;
To call the Javascript, build a javascript function call into a string, and then call the page()->runJavascript() function.
QString command = QString("javascriptFunction(%1);").arg(functionParameter) ;
page()->runJavaScript(command) ;

To receive messages from Javascript, public slots must be used:
public slots:
    void updateComplete(int x) ;

Javascript

To initialise, the following script line should be included:
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
Then the <body> tag should have an onload="initialise()" option.
In the initialise function, the other end of the communications link should be set up, noting that the setting of (in this case) widget is asynchronous to the initialise function call.  Note also that 'widget' should be a global variable.
var widget ;

function initialise() {
  if (typeof qt != 'undefined') new QWebChannel(qt.webChannelTransport, function(channel) {
    widget = channel.objects.widget;
  } );
}
Some time after the initialisation, the widget variable will be defined (it will be of type QObject).  You can emit signals to C++ simply by calling the appropriate function, so for example:
widget.updateComplete(x) ;
Will emit a signal which will be captured some time later in the updateComplete(int x) slot in the C++ class. You need to appreciate that all calls between C++ and Javascript are asynchronous with QtWebEngineView.


1 comment:

  1. Very excellent article which helped me transition, very easily, from my experience with WebKit to the new WebEngine.
    Thank you so much for a great tutorial.
    aouhlal

    ReplyDelete