我正在使用新的WebEngine来玩游戏并学习。
我一直在尝试使用Qt WebKit找到一些类似的方法:addToJavaScriptWindowObject()
我发现使用Qt WebEngine,我必须使用QWebChannel
向Javascript窗口对象注册函数。如果这是正确的,我会回答以下问题。
我在我的电脑上安装了Qt 5.4.0。我注意到在我的计算机上安装的SDK中找不到qwebchannel.js
。我在Git源代码中找到了它。
如果我有一个带有QWebEnginePage
和QWebEngineView
的Qt原生桌面应用程序,我需要在Javascript窗口对象上注册函数吗?
我的桌面应用程序自动导航到我创建的http页面。因此,我可以访问与QWebEngineView
相关联的内容。
采取了哪些措施让我能够做到这一点?
答案 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)
我将总结您的问题如下:
首先,让我们用一个简单的代码来玩:
#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
中来自“维基百科”的页面,并连接了信号/插槽事件以在页面最终加载时打印一些日志。
您可以简单地使用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
的默认上下文冲突。该脚本将每个链接的背景颜色更改为黄色。
在这种情况下,我们需要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应用添加QWebSocketServer
,QWebEngineView
的HTML / Javascript将使用WebSocket连接到该应用。然后使用QWebChannel
进行双向通信。
答案 3 :(得分:0)
与页面进行通信的另一种更简单的方法是使用runJavaScript
函数:
view->page()->runJavaScript("alert('Hello from C++');");
它有其局限性:调用必须从C ++端启动,并且您只能从JS获得同步响应。但是也有一个好处:无需修改基础网页。
可以使用QWebEngineView::page()
函数访问当前打开的网页,如上例所示。在导航期间,浏览器不会更改页面,直到从网络接收到下一页为止,因此此函数随时返回有效的页面对象。但是,您的JS可能仍会以一种出现在document.readyState == 'loading'
中的方式来中断新页面的加载,其中DOM树尚未构建,并且页面上的某些脚本可能尚未运行。在这种情况下,您应该等待DOMContentLoaded
事件。