How to use Qt WebEngine and QWebChannel?

前端 未结 4 1156
孤街浪徒
孤街浪徒 2020-12-03 01:48

I\'m using the new WebEngine to play around and learn. I\'ve been trying to find some similar methods found using Qt WebKit: addToJavaScriptWindowObject()

相关标签:
4条回答
  • 2020-12-03 02:10

    An alternative and much simpler way of communication with the page is to use runJavaScript function:

    view->page()->runJavaScript("alert('Hello from C++');");
    

    It has its limitations: the call must be initiated from the C++ side and you can get only synchronous response from JS. But there is an upside too: no modification of the underlying webpage is necessary.

    Currently opened webpage can be accessed using QWebEngineView::page() function, as in the example above. During the navigation, the browser doesn't change the page until the next one is received from the network, so this function returns valid page object at any time. But your JS may still interrupt new page loading in a way that you'll appear in the document.readyState == 'loading' where the DOM tree is not yet constructed and some scripts on the page might not have been run yet. In this case you should wait for the DOMContentLoaded event.

    0 讨论(0)
  • Qt has documentation on this now:

    Qt WebChannel Standalone Example

    You have to add a QWebSocketServer to your cpp app that the QWebEngineView's HTML/Javascript will connect to using a WebSocket. Then use QWebChannel for two way communication.

    0 讨论(0)
  • 2020-12-03 02:24

    In Qt5.6, if you want to make C++ part and JavaScript to communicate, the only way to do it is using QWebChannel on a QWebEngineView, as you stated. You do it this way in the .cpp file:

    m_pView = new QWebEngineView(this);
    QWebChannel * channel = new QWebChannel(page);
    m_pView->page()->setWebChannel(channel);
    channel->registerObject(QString("TheNameOfTheObjectUsed"), this);
    

    Here, you just say that you register an object named TheNameOfTheObjectUsed that will be available on the JS side. Now, this is the part of code to use in the JS side :

    new QWebChannel(qt.webChannelTransport, function (channel) {
                // now you retrieve your object
                var JSobject = channel.objects.TheNameOfTheObjectUsed;
            });
    

    Now, if you want to retrieve some properties of the class in the JS side, you need to have a method on the C++ side which returns a string, an integer, a long... This is what it looks like on the C++ side, in your .h:

    Q_INVOKABLE int getInt();
    Q_PROPERTY(int myIntInCppSide READ getInt);
    

    And now, you get the int like this on the JS side :

    var myIntInJSside= JSobject.myIntInCppSide;
    

    This is a very simple explanation, and I recommend you to watch this video which was very useful to me. Also, you might want to read more about the JavaScript API provided by QWebChannel, as well as the documentation about QWebChannel.

    Hope that helps!

    0 讨论(0)
  • 2020-12-03 02:27

    I will summarize your questions as following:

    1. Do I need QWebChannel to register JavaScript functions in the WebEngine?
    2. Where can I find QWebChannel.js
    3. How to communicate JS to C++ and C++ to JS

    First, let take a simple code to play with:

    #include <QApplication>
    #include <QDebug>
    #include <QWebEngineView>
    #include <QWebChannel>
    
    // ... DEFINITIONS HERE
    
    auto main( int argn, char* argv[] )-> int
    {
        QApplication app(argn, argv);
        QWebEngineView browser;
        browser.resize(QSize(800,600));
        browser.show();
        browser.load(QUrl("http://www.wikipedia.org"));
    
        // .. SETUP HERE
    
        QObject::connect(&browser, &QWebEngineView::loadFinished, [&browser](bool ok)
        { 
            qDebug()<<"Load Finished " << ok;
    
            // TEST CODE HERE
        ));
    
        return app.exec();
    }
    

    Explanation: This code creates a Qt application, creates a QWebEngineView and set some minimal properties to make it visible. A page from 'Wikipedia' is loaded inside and a signal/slot event is connected to print some log when the page is finally loaded.

    How to call JS functions from C++ ?

    You can simply call JS using QWebEnginePage::runJavaScript as following. Add this code to the TEST CODE HERE.

    QString code = QStringLiteral(
    R"DELIM(
    
    var links = document.getElementsByTagName('a');
    for ( var i=0; i<links.length; ++i)
    {
        links[i].style.backgroundColor = 'yellow';
    };
    )DELIM");
    browser.page()->runJavaScript(code, 42);
    

    Explanation: This code execute some JS into the browser, on a context ID 42, avoiding collision with the default context of the page ID 0. The script change the background-color of each link to yellow.

    How to call C++ from JS?

    In this case, we need the QWebChannel mechanism to register C++ objects into JavaScript.

    First, let create the C++ interface, callable from JS (in DEFINITION):

    class JsInterface: public QObject
    {
        Q_OBJECT
    public:
        /// Log, for debugging
        Q_INVOKABLE void log(const QString& str) const
        {
            qDebug() << "LOG from JS: " << str;
        }
    };
    #include "main.moc"
    

    Explanation: This code declare and define a QObject class with a simple log function inside. It is important to declare the function Q_INVOKABLE otherwise JavaScript can not find it!. As the declaration is inside the same file as the rest of the code, we include the auto-moc file from QT after (it is main.moc because my file is main.cpp).

    Create a function in DEFINITION which return the JavaScript QWebChannel.js content. The content of QWebChannel.js can be found in your QT library (./5.12.2/Src/qtwebchannel/examples/webchannel/shared/qwebchannel.js or ./Examples/Qt-5.12.2/webchannel/shared/qwebchannel.js). You are free to load this directly in your page.

    In DECLARATION section, append:

    QString qWebChannelJs()
    {
        return R"DELIMITER(
        // COPY HERE ALL THE FILE
        )DELIMITER";
    }
    

    And we inject it in our code (Append it to TEST CODE HERE section):

    browser.page()->runJavaScript(qWebChannelJs(), 42);
    

    We need to setup the QWebChannel in C++ side (SETUP section):

    QWebChannel channel;
    JsInterface jsInterface;
    browser.page()->setWebChannel(&channel, 42);
    channel.registerObject(QString("JsInterface"), &jsInterface);
    

    Explanation: We create a channel, the JsInterface object and register them into the browser. We need to use the same context id 42 (but could be another other number between 0 and 255).

    Finally, in our JS code, we access the channel and call the function of the interface (append to TEST CODE section):

    QString code2 = QStringLiteral(
    R"DELIM(
    
    window.webChannel = new QWebChannel(qt.webChannelTransport, function( channel)
    {
        var cpp = channel.objects.JsInterface;
        cpp.log("Hello from JavaScript");
    });
    
    )DELIM");
    browser.page()->runJavaScript(code2, 42);
    

    Considerations

    It is worth to mention that any call from C++ to JavaScript or from JavaScript to C++ goes through an Inter-Process-Communication (IPC) which is asynchronous. This means that runJavaScript returns before the JavaScript is executed, and that JavaScript returns before the C++ logis executed.

    Full code

    #include <QApplication>
    #include <QDebug>
    #include <QWebEngineView>
    #include <QWebChannel>
    
    QString qWebChannelJs()
    {
        return R"DELIMITER(
            // TODO INSERT JS code here
        )DELIMITER";
    }
    
    class JsInterface: public QObject
    {
        Q_OBJECT
    public:
        /// Log, for debugging
        Q_INVOKABLE void log(const QString& str) const
        {
            qDebug() << "LOG from JS: " << str;
        }
    };
    #include "main.moc"
    
    auto main( int argn, char* argv[] )-> int
    {
        QApplication app(argn, argv);
        QWebEngineView browser;
        browser.resize(QSize(800,600));
        browser.show();
        browser.load(QUrl("http://www.wikipedia.org"));
    
        // .. SETUP HERE
        QWebChannel channel;
        JsInterface jsInterface;
        browser.page()->setWebChannel(&channel, 42);
        channel.registerObject(QString("JsInterface"), &jsInterface);
    
        QObject::connect(&browser, &QWebEngineView::loadFinished, [&browser](bool ok)
        { 
            qDebug()<<"Load Finished " << ok;
    
            // TEST CODE HERE
            QString code = QStringLiteral(
            R"DELIM(
    
            var links = document.getElementsByTagName('a');
            for ( var i=0; i<links.length; ++i)
            {
                links[i].style.backgroundColor = 'yellow';
            };
    
            )DELIM");
            browser.page()->runJavaScript(code, 42);
    
            browser.page()->runJavaScript(qWebChannelJs(), 42);
    
            QString code2 = QStringLiteral(
            R"DELIM(                   
            window.webChannel = new QWebChannel(qt.webChannelTransport, function( channel)
            {
                var cpp = channel.objects.JsInterface;
                cpp.log("Hello from JavaScript");
            });
    
            )DELIM");
            browser.page()->runJavaScript(code2, 42);
        });
    
        return app.exec();
    }
    

    Related topics:

    How to setup QWebChannel JS API for use in a QWebEngineView?

    External documentation:

    https://doc.qt.io/qt-5/qwebengineview.html
    https://doc.qt.io/qt-5/qwebchannel.html
    https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-contentmanipulation-example.html

    0 讨论(0)
提交回复
热议问题