javascriptc++qtqwebviewqtwebview

How to use QML QtWebView to call C++?


With Qt 5.5, using the Minibrowser example they provide, it uses something different than QWebView widget. Instead, it uses QML and a QtWebView module. When you look in Javascript's navigator.appVersion, it lets you know that QWebView loads a custom AppleWebKit/538.1 (a thing shipped with Qt5.5), while QtWebView (note the difference) loads the native core OS AppleWebKit/601.1.56. This is confirmed because when I load Safari on my OSX (El Capitan version), it says 601.1.56.

The problem, however, is how can my Javascript functions in Minibrowser call C++ functions on the backend to do more powerful stuff? When I was using the QWebView widget, I was able to use the C++ webkit bridge that let me inject into the DOM my C++ object and therefore could call my C++ code. I'm not seeing any technique documented for how to do this with the QML-based Minibrowser example that uses QtWebView. What's the technique?

EDIT: Oh, and to clarify, I'm not calling remote web pages with this, via a web server. I'm just calling stuff through file://. In other words, I'm using the rich webkit interface to give me a super powerful GUI that goes way beyond what the Qt widgets and QML can provide for me.


Solution

  • I found the answer. Basically, in Qt5.5, I'm diving into experimental land with QtWebView. The class methods are largely undocumented and may change in the future.

    For now, the only technique is message passing, not native C++ class method invocation like you could use in the QWebView C++ Bridge. The QWebView C++ Bridge only works with QWebView widgets, not the QtWebView QML. So, you do this message passing with navigator.qt.postMessage() API from your Javascript to your QML, and then QML can call C++. In order to get that extra functionality, you'll need to do a few steps. Here's an example:

    Invoke C++ method from webviews Javascript

    This is blogged a little here:

    http://rschroll.github.io/beru/2013/08/21/qtwebview.experimental.html

    As you can see from the example, you have to add these imports to your main.qml:

    import QtWebKit 3.0
    import QtWebKit.experimental 1.0
    

    Then, in your WebView{} section, you have to add this line:

    experimental.preferences.navigatorQtObjectEnabled: true
    

    At that point, you can call navigator.qt.postMessage("call foo in C++"); to send a message to the QML, which you can then pick up inside your WebView{} section with:

    experimental.onMessageReceived: { ...do something here in the QML... }
    

    Your QML can then pass a message right back to the Javascript with:

    experimental.postMessage("okay, I called foo in C++")
    

    And then in your Javascript you can add an event listener like so:

    navigator.qt.onmessage = function(ev) {
    
      $('BODY').prepend(ev.data); // since console.log() is not possible
    
    }
    

    As for how to get your QML to call C++, here's an example:

    https://stackoverflow.com/a/17881019/105539

    EDIT: After more experimentation in Qt 5.5 with QtWebView, it appears fairly flaky in the following respects. I don't recommend it in 5.5 -- it's not ready for primetime. In Qt 5.5, you're better off using the QWebView widget for now and then migrate to QtWebEngine when the next version of Qt comes out (Qt 5.6, 5.7?).