我正在使用新的WebEngine进行游戏和学习。我一直在尝试使用Qt WebKit找到一些类似的方法:addToJavaScriptWindowObject()
我发现使用QtWebEngine,我必须使用QWebChannel
将函数注册到JavaScript窗口对象。如果这是正确的,我就要回答以下问题。
我在电脑上安装了Qt 5.4.0。我注意到在我电脑上安装的SDK中找不到qwebchannel.js
。我在Git源代码上找到的。
如果我有一个带有QWebEnginePage
和QWebEngineView
的Qt原生桌面应用程序,那么我需要什么才能在JavaScript窗口对象上注册函数?
我的桌面应用程序会自动导航到我创建的http页面。因此,我可以访问连接到QWebEngineView
的内容。
我要采取哪些步骤才能使之发挥作用?
在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;
这是一个非常简单的解释,我建议您观看这段视频,这对我非常有用。此外,您可能想阅读更多关于QWebChannel提供的JavaScript API的信息,以及有关QWebChannel的文档。
希望能有所帮助!
我将把您的问题总结如下:
- 我需要QWebChannel在WebEngine中注册JavaScript函数吗
- 在哪里可以找到QWebChannel.js
- 如何将JS与C++和C++与JS进行通信
我将逐步解释代码。在下面的代码段中,有标记为
DEFINITIONS
、SETUP
和TEST
的部分。以下代码将插入其中。
首先,让我们使用一个简单的代码:
#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);
说明:这段代码在浏览器中执行一些JS,上下文ID为42
,避免了与页面ID 0
的默认上下文发生冲突。脚本将每个链接的背景颜色更改为黄色。
如何从JS调用C++
在这种情况下,我们需要QWebChannel机制将C++对象注册到JavaScript中。
首先,让我们创建一个C++接口,可从JS(在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/sshared/QWebChannel.js)。您可以直接在页面中加载。
在DECLARATION
部分,附加:
QString qWebChannelJs()
{
return R"DELIMITER(
// COPY HERE ALL THE FILE
)DELIMITER";
}
我们在代码中注入它(将它附加到TEST CODE HERE
部分):
browser.page()->runJavaScript(qWebChannelJs(), 42);
我们需要在C++侧(SETUP
部分)设置QWebChannel
:
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
执行之前返回。
JsInterface
对象创建一次,在将通道设置为页面(setWebChannel
)之前,必须将registerObject
放入QWebChannel
中。然后,每次加载页面时,都会加载js API(runJavaScript(qWebChannelJs())
),并将通道设置为该页面。如果在通道设置后注册JsInterface
,则会收到以下日志消息:
初始化后注册了新对象,不会通知现有客户端!
完整代码
#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();
}
相关主题:
如何设置在QWebEngineView中使用的QWebChannel JS API?
外部文件:
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
Qt现在有关于这方面的文档:
Qt WebChannel独立示例
您必须向您的cpp应用程序添加一个QWebSocketServer
,QWebEngineView
的HTML/Javascript将使用WebSocket连接到该应用程序。然后使用QWebChannel
进行双向通信。
与页面通信的另一种更简单的方式是使用runJavaScript
函数:
view->page()->runJavaScript("alert('Hello from C++');");
它有其局限性:调用必须从C++端启动,并且只能从JS获得同步响应。但也有好处:不需要修改底层网页。
可以使用QWebEngineView::page()
功能访问当前打开的网页,如上面的示例所示。在导航过程中,浏览器不会更改页面,直到从网络接收到下一个页面,因此此函数随时返回有效的页面对象。但是您的JS可能仍然会中断新页面加载,就像您在document.readyState == 'loading'
中出现的那样,其中DOM树尚未构建,页面上的一些脚本可能尚未运行。在这种情况下,您应该等待DOMContentLoaded
事件。