I will summarize your questions as following:
- Do I need QWebChannel to register JavaScript functions in the WebEngine?
- Where can I find QWebChannel.js
- How to communicate JS to C++ and C++ to JS
I will be explaining the code step-by-step. In the following snippet, there are sections marked as DEFINITIONS
, SETUP
, and TEST
. Following code will be inserted there.
First, let take a simple code to play with:
#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();
}
Explanation: This code creates a Qt application, creates a QWebEngineView and set some minimal properties to make it visible.
A page from 'Wikipedia' is load
ed inside and a signal/slot event is connected to print some log when the page is finally loaded.
How to call JS functions from C++ ?
You can simply call JS using QWebEnginePage::runJavaScript
as following. Add this code to the 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);
Explanation: This code execute some JS into the browser, on a context ID 42
, avoiding collision with the default context of the page ID 0
. The script change the background-color of each link to yellow.
How to call C++ from JS?
In this case, we need the QWebChannel mechanism to register C++ objects into JavaScript.
First, let create the C++ interface, callable from JS (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"
Explanation: This code declare and define a QObject class with a simple log
function inside. It is important to declare the function Q_INVOKABLE
otherwise JavaScript can not find it!. As the declaration is inside the same file as the rest of the code, we include the auto-moc file from QT after (it is main.moc
because my file is main.cpp
).
Create a function in DEFINITION
which return the JavaScript QWebChannel.js
content. The content of QWebChannel.js can be found in your QT library (./5.12.2/Src/qtwebchannel/examples/webchannel/shared/qwebchannel.js or ./Examples/Qt-5.12.2/webchannel/shared/qwebchannel.js). You are free to load this directly in your page.
In DECLARATION
section, append:
QString qWebChannelJs()
{
return R"DELIMITER(
// COPY HERE ALL THE FILE
)DELIMITER";
}
And we inject it in our code (Append it to TEST CODE HERE
section):
browser.page()->runJavaScript(qWebChannelJs(), 42);
We need to setup the QWebChannel
in C++ side (SETUP
section):
QWebChannel channel;
JsInterface jsInterface;
browser.page()->setWebChannel(&channel, 42);
channel.registerObject(QString("JsInterface"), &jsInterface);
Explanation: We create a channel, the JsInterface
object and register them into the browser. We need to use the same context id 42
(but could be another other number between 0 and 255).
Finally, in our JS code, we access the channel and call the function of the interface (append to TEST CODE
section):
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);
Considerations
It is worth to mention that any call from C++ to JavaScript or from JavaScript to C++ goes through an Inter-Process-Communication (IPC) which is asynchronous. This means that runJavaScript
returns before the JavaScript is executed, and that JavaScript returns before the C++ log
is executed.
The JsInterface
object is created once and must be registerObject
into the QWebChannel
before setting the channel to the page (setWebChannel
). Then, every time a page is loaded, the js API is loaded (runJavaScript(qWebChannelJs())
) and the channel is set to the page. If you register the JsInterface
after the channel is set, you will get the following log message:
Registered new object after initialization, existing clients won't be notified!
Full code
#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();
}
Related topics:
How to setup QWebChannel JS API for use in a QWebEngineView?
External documentation:
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