How to use Qt WebEngine and QWebChannel?

前端 未结 4 1158
孤街浪徒
孤街浪徒 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: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 
    #include 
    #include 
    #include 
    
    // ... 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; irunJavaScript(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 
    #include 
    #include 
    #include 
    
    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; irunJavaScript(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

提交回复
热议问题