18

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

我发现使用 Qt WebEngine,我必须使用将QWebChannel函数注册到 JavaScript 窗口对象。如果这是正确的,它将带我进入以下问题。

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

如果我有一个带有QWebEnginePageand的 Qt 本机桌面应用程序QWebEngineView,我需要什么才能在 JavaScript 窗口对象上注册函数?

我的桌面应用程序会自动导航到我创建的 http 页面。所以我可以访问连接到QWebEngineView.

需要采取哪些步骤才能完成这项工作?

4

4 回答 4

19

在 Qt5.6 中,如果您想让 C++ 部分和 JavaScript 进行通信,唯一的方法是在QWebEngineView上使用QWebChannel,如您所述。你在文件中这样做.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++ 端有一个方法,它返回一个字符串、一个整数、一个 long ......这就是它在 C++ 端的样子,在你的.h

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

现在,你在 JS 端得到这样的 int :

var myIntInJSside= JSobject.myIntInCppSide;

这是一个非常简单的解释,我建议您观看这个对我非常有用的视频。此外,您可能想阅读更多关于 QWebChannel 提供的JavaScript API以及关于QWebChannel的文档。

希望有帮助!

于 2016-04-13T14:13:21.093 回答
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 上执行一些 JS 到浏览器中42,避免与页面 ID 的默认上下文冲突0。该脚本将每个链接的背景颜色更改为黄色。

如何从 JS 调用 C++?

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

首先,让我们创建可从 JS 调用的 C++ 接口(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"

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

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

DECLARATION部分中,附加:

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

我们将它注入到我们的代码中(将其附加到TEST CODE HERE部分):

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

我们需要QWebChannel在 C++ 端(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();
}

相关话题:

如何设置 QWebChannel JS API 以在 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

于 2020-06-05T07:43:27.053 回答
0

Qt 现在有这方面的文档:

Qt WebChannel 独立示例

您必须向QWebSocketServer您的 cpp 应用程序添加一个QWebEngineViewHTML/Javascript 将使用 WebSocket 连接的应用程序。然后QWebChannel用于双向通信。

于 2015-12-02T18:02:21.377 回答
0

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

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

它有它的局限性:调用必须从 C++ 端发起,并且只能从 JS 获得同步响应。但也有一个好处:不需要修改底层网页。

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

于 2020-04-03T16:59:20.723 回答