如何使用Qt WebEngine和QWebChannel?

时间:2015-02-17 15:42:19

标签: javascript c++ qt

我正在使用新的WebEngine来玩游戏并学习。 我一直在尝试使用Qt WebKit找到一些类似的方法:addToJavaScriptWindowObject()

我发现使用Qt WebEngine,我必须使用QWebChannel向Javascript窗口对象注册函数。如果这是正确的,我会回答以下问题。

我在我的电脑上安装了Qt 5.4.0。我注意到在我的计算机上安装的SDK中找不到qwebchannel.js。我在Git源代码中找到了它。

如果我有一个带有QWebEnginePageQWebEngineView的Qt原生桌面应用程序,我需要在Javascript窗口对象上注册函数吗?

我的桌面应用程序自动导航到我创建的http页面。因此,我可以访问与QWebEngineView相关联的内容。

采取了哪些措施让我能够做到这一点?

4 个答案:

答案 0 :(得分:14)

在Qt5.6中,如果你想让C ++部分和JavaScript进行通信,唯一的方法就是在QWebChannel上使用QWebEngineView,如你所说。你可以在.cpp文件中这样做:

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

在这里,您只需要注册一个名为TheNameOfTheObjectUsed的对象,该对象将在JS端提供。现在,这是JS方面使用的代码部分:

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

现在,如果你想在JS端检索类的一些属性,你需要在C ++端有一个方法,它返回一个字符串,一个整数,一个长...这就是它的样子C ++方面,在.h

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

现在,你在JS方面得到了这样的int:

var myIntInJSside= JSobject.myIntInCppSide;

这是一个非常简单的解释,我建议你观看对我来说非常有用的this video。另外,您可能希望详细了解QWebChannel提供的JavaScript API以及有关QWebChannel的文档。

希望有所帮助!

答案 1 :(得分:4)

我将总结您的问题如下:

  1. 我需要QWebChannel在WebEngine中注册JavaScript函数吗?
  2. 在哪里可以找到QWebChannel.js
  3. 如何将JS与C ++通信以及如何将C ++与JS通信

首先,让我们用一个简单的代码来玩:

#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();
}

说明:此代码创建一个Qt应用程序,创建一个QWebEngineView并设置一些最小的属性以使其可见。 内置load中来自“维基百科”的页面,并连接了信号/插槽事件以在页面最终加载时打印一些日志。

如何从C ++调用JS函数?

您可以简单地使用QWebEnginePage::runJavaScript来调用JS,如下所示。将此代码添加到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);

说明:此代码在上下文ID 42上向浏览器执行一些JS,避免与页面ID 0的默认上下文冲突。该脚本将每个链接的背景颜色更改为黄色。

如何从JS调用C ++?

在这种情况下,我们需要QWebChannel机制将C ++对象注册到JavaScript中。

首先,让我们创建可通过JS(在DEFINITION中调用)的C ++接口:

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"

说明:此代码声明并定义一个QObject类,其中包含一个简单的log函数。声明函数Q_INVOKABLE很重要,否则JavaScript找不到它!由于该声明与其余代码位于同一文件内,因此我们在QT之后包括来自QT的auto-moc文件(它是main.moc,因为我的文件是main.cpp)。

DEFINITION中创建一个函数,该函数返回JavaScript QWebChannel.js的内容。可以在您的QT库(./5.12.2/Src/qtwebchannel/examples/webchannel/shared/qwebchannel.js或./Examples/Qt-5.12.2/webchannel/shared/qwebchannel)中找到QWebChannel.js的内容。 js)。您可以将其直接直接加载到页面中。

DECLARATION部分中,附加:

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

然后将其注入代码中(将其添加到TEST CODE HERE部分):

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

我们需要在C ++端(QWebChannel部分)中设置SETUP

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

说明:我们创建一个频道JsInterface对象,并将其注册到浏览器中。我们需要使用相同的上下文ID 42(但可以是0到255之间的另一个数字)。

最后,在我们的JS代码中,我们访问通道并调用接口的函数(附加到TEST CODE部分):

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);

注意事项

值得一提的是,从C ++到JavaScript或从JavaScript到C ++的任何调用都要经过异步的进程间通信(IPC)。这意味着runJavaScript在执行JavaScript之前返回,而JavaScript在C ++ log执行之前返回。

完整代码

#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();
}

相关主题:

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

外部文档:

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

答案 2 :(得分:0)

Qt现在有这方面的文件:

Qt WebChannel Standalone Example

您必须向cpp应用添加QWebSocketServerQWebEngineView的HTML / Javascript将使用WebSocket连接到该应用。然后使用QWebChannel进行双向通信。

答案 3 :(得分:0)

与页面进行通信的另一种更简单的方法是使用runJavaScript函数:

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

它有其局限性:调用必须从C ++端启动,并且您只能从JS获得同步响应。但是也有一个好处:无需修改基础网页。

可以使用QWebEngineView::page()函数访问当前打开的网页,如上例所示。在导航期间,浏览器不会更改页面,直到从网络接收到下一页为止,因此此函数随时返回有效的页面对象。但是,您的JS可能仍会以一种出现在document.readyState == 'loading'中的方式来中断新页面的加载,其中DOM树尚未构建,并且页面上的某些脚本可能尚未运行。在这种情况下,您应该等待DOMContentLoaded事件。