12

I'm using the QT WebEngine framework to display web pages. I'm injecting javascript into a page when it loads, and want to allow the javascript to be able to access a QT object. Apparently, to do this a QWebChannel must exist that establishes some IPC between chromium (the javascript) and the rest of my C++/QT project. I came across the QWebEnginePage::setWebChannel (QWebChannel *channel) function, however I can't find any examples of its use. The documentation (http://doc.qt.io/qt-5/qwebenginepage.html#setWebChannel) mentions that qt.webChannelTransport should be available in the javascript context, but I don't see where that is established in qwebchannel.js (https://github.com/qtproject/qtwebchannel/blob/dev/src/webchannel/qwebchannel.js). I've seen the WebChannel examples (http://doc.qt.io/qt-5/qtwebchannel-examples.html) and would like to avoid WebSockets if possible.

Below is how I tried to implement the web channel.

Whenever a page loads I establish a channel and inject the javascript in C++:

QWebChannel *channel = new QWebChannel();
channel->registerObject(QStringLiteral("jshelper"), helper);

view->page()->runJavaScript(qwebjs); //this is qwebchannel.js
view->page()->setWebChannel(channel);
view->page()->runJavaScript(myfunction); //function that calls QT object (jshelper)

In Javascript:

new QWebChannel(qt.webChannelTransport, function(channel) { ... });

Which results in the channel not being connected properly (assuming this is because of qt.webChannelTransport, as it was working when I was using WebSockets). Any pointers to examples of QWebChannel being set up with QWebEnginePage this way is also appreciated.

Vicky Chijwani
  • 10,191
  • 6
  • 56
  • 79
mathieujofis
  • 349
  • 3
  • 16

1 Answers1

29

Short answer: add <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> to your html page (before you call new QWebChannel of course), and remove the line view->page()->runJavaScript(qwebjs); //this is qwebchannel.js from your C++ code.

Long answer:

I too had a ton of trouble figuring out how to use QWebChannel without WebSockets correctly -- managed to get it working after digging around in Qt 5.5 source code and mailing lists (documentation is still lacking). Note that this only works with the new Qt 5.5.

Here's how to use QWebChannel:

// file: MyWebEngineView.cpp, MyWebEngineView extends QWebEngineView
QWebChannel *channel = new QWebChannel(page());

// set the web channel to be used by the page
// see http://doc.qt.io/qt-5/qwebenginepage.html#setWebChannel
page()->setWebChannel(channel);

// register QObjects to be exposed to JavaScript
channel->registerObject(QStringLiteral("jshelper"), helper);

// now you can call page()->runJavaScript(...) etc
// you DON'T need to call runJavaScript with qwebchannel.js, see the html file below

// load your page
load(url);

And on the JS side:

<!-- NOTE: this is what you're missing -->
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>

<script type="text/javascript">
    <!-- it's a good idea to initialize webchannel after DOM ready, if your code is going to manipulate the DOM -->
    document.addEventListener("DOMContentLoaded", function () {
        new QWebChannel(qt.webChannelTransport, function (channel) {
            var jshelper = channel.objects.jshelper;
            // do what you gotta do
        });
    });
</script>

Also make sure you've added QT += webenginewidgets webchannel to your .pro file else this won't build!

Bonus: you can debug your JavaScript from the comfort of Chrome Dev Tools now! Just add this somewhere in your Qt code (ideally in your application startup):

#ifdef QT_DEBUG
    qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "23654");
#endif

Then start your application, navigate to http://localhost:23654 in Chrome, and you'll get a fully-functional JS debugger, profiler, console, etc :)


Follow-up (19/04/2016): if your remote debugger isn't working, note that the qputenv call must also occur before any calls to QWebEngineSettings or any other WebEngine-related class, because these trigger the WebEngine "zygote" process immediately (the zygote is the parent QtWebEngineProcess from which all future QtWebEngineProcesses are forked) and then qputenv cannot affect it. Spent a few hours tracking this down.

Vicky Chijwani
  • 10,191
  • 6
  • 56
  • 79
  • This is great-- my only problem is I have to inject the scripts as I don't have any control over the pages that are being loaded. Also, when you say "the new QT 5.5", do you mean from the dev branch? I'm using QT Creator 5.5.0 and I'm not sure how updated the QT source code that I'm working from is. I tried the environment variable setting to debug JS in a console but it's not working (I think because this debug option is from a recent commit). – mathieujofis Aug 13 '15 at 00:50
  • I see. In Qt Creator, go to Tools > Options > Build & Run > Qt Versions and check what version you have. When I say Qt 5.5, I mean [the stable version that was released on July 1st of this year](http://blog.qt.io/blog/2015/07/01/qt-5-5-released/). Qt 5.5 added built-in support for Chromium IPC over QWebChannel (i.e., `QWebEnginePage::setWebChannel()` and `qt.webChannelTransport` in JS). Also, your Qt Creator cannot be at v5.5, [the latest version is v3.4.2](http://blog.qt.io/blog/2015/07/01/qt-creator-3-4-2-released/) :) – Vicky Chijwani Aug 13 '15 at 08:11
  • Also, the `QTWEBENGINE_REMOTE_DEBUGGING` is present in the Qt 5.5 release: http://stackoverflow.com/a/29721197/504611 – Vicky Chijwani Aug 13 '15 at 08:13
  • Eventually worked this out-- the remote debugging really helped. This was the correct answer for the most part, except for in my situation I did end up injecting the qwebchannel.js, which works well. – mathieujofis Nov 01 '15 at 21:39
  • 1
    note: in destructor if helper has parent dergister helper; page()->webChannel()->deregisterObject(helper); – khani_mahdi Jan 26 '16 at 05:46
  • I have a problem with `qt` object which is lost after page reload: `js: Uncaught ReferenceError: qt is not defined`. I tried to create new channel and set it to page on load page slot, but it doesn't work – Oleh Pomazan Jul 13 '16 at 07:43
  • 1
    @OlehPomazan yes I experienced the same thing: webchannel doesn't work well with page reloads. Maybe they will fix this in a later version. – Vicky Chijwani Jul 13 '16 at 08:14
  • @VickyChijwani Thank you so much for your succinct answer! I was having the exact problem of communicating from JS to C++ objects in Qt 5.7.0. Now it works like a charm. Thanks again! – Manmohan Singh Aug 10 '16 at 07:33
  • @mathieujofis any chance you can post the code that worked for you? cause other than the code injection part your code and Vicky's code is pretty identical and I too need to inject both the qwebchannel.js and the other JS code as well. Currently I get the callback in JS and the jshelper object is defined but not its functions or properties – Anand Aug 10 '17 at 17:49
  • It's maybe worth pointing out that `QTWEBENGINE_REMOTE_DEBUGGING` is just an ordinary environment variable. Sure you can set it with `qputenv()`, but on a POSIX system you can also just launch your application from a terminal with `QTWEBENGINE_REMOTE_DEBUGGING=54321 ./myApp`. Any method of setting an envvar will work, there's no need to even modify the code (unless you want to). – FeRD Aug 30 '20 at 03:35