【问题标题】:How to use Qt WebEngine and QWebChannel?如何使用 Qt WebEngine 和 QWebChannel?
【发布时间】:2015-04-18 08:53:23
【问题描述】:

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

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

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

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

我的桌面应用程序会自动导航到我创建的 http 页面。这样我就可以访问到QWebEngineView的内容了。

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

【问题讨论】:

  • 你能找到解决办法吗?我遇到了同样的问题,我想要从我的网页到 c++ 的某种通信(如果可能的话,通过 js 或任何其他方式)请帮助..

标签: javascript c++ qt qtwebengine qtwebchannel


【解决方案1】:

在 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++ 端有一个方法,它返回一个字符串、一个整数、一个长整数......这就是它的样子C++ 方面,在你的.h:

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

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

var myIntInJSside= JSobject.myIntInCppSide;

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

希望有帮助!

【讨论】:

  • 你在哪里找到关于qt.webChannelTransport 的那部分内容?我在文档或示例中的任何地方都找不到它...
  • @sschober 你可以在QWebEnginePage doc找到它:The transport is exposed in the JavaScript world as qt.webChannelTransport, which should be used when using the Qt WebChannel JavaScript API.
【解决方案2】:

我将您的问题总结如下:

  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 并设置一些最小属性以使其可见。 'Wikipedia' 的页面在内部是loaded,并且在页面最终加载时连接了一个信号/插槽事件以打印一些日志。

如何在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 调用的 C++ 接口(在 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 中创建一个函数,该函数返回 JavaScript QWebChannel.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);

我们需要在 C++ 端设置QWebChannelSETUP 部分):

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

【讨论】:

    【解决方案3】:

    Qt 现在有这方面的文档:

    Qt WebChannel Standalone Example

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

    【讨论】:

      【解决方案4】:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-08
        相关资源
        最近更新 更多