Reputation: 93
Based on QT QWebEnginePage::setWebChannel() transport object and Qt: Cannot invoke shared object methods/properties from javascript I tried to make a small demo to test the functionality. See the basic test code below. My questions are:
.pro file
QT += core gui
QT += network webenginewidgets webchannel widgets
TARGET = hfbTestWebChannel
TEMPLATE = app
SOURCES += hfbTestWebChannel.cpp
RESOURCES += hfbTestWebChannel.qrc
.html file
<html>
<body>
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript">
var theQtObj;
function buttonA()
{
PrintLog("Button A:init")
if (typeof qt !== "undefined")
{
new QWebChannel(qt.webChannelTransport, function(channel)
{
theQtObj = channel.objects.theQtObj;
PrintLog(" done, now theQtObj=" + theQtObj.toString());
});
}
else
PrintLog("!!!qt undefined!!!" );
}
function buttonB()
{
PrintLog("Button B : call c++ and get some answers!")
if(typeof theQtObj !== "undefined")
{
var X = prompt("Enter a number", "5");
PrintLog("theQtObj=" + theQtObj + " => X=" + X);
var n = theQtObj.getInt(X);
PrintLog(" back in js with n="+n);
var d = theQtObj.getDouble(n);
PrintLog(" back in js with d="+d);
var s = theQtObj.getString(d.toString());
PrintLog("X:" + X + "->" + n + "->" + d + "->" + s);
}
else
PrintLog(" --> theQtObj not defined");
}
function buttonC()
{
PrintLog("Button C:get c++ object member elements")
if(typeof theQtObj !== "undefined")
{
PrintLog("theQtObj._theString=" + theQtObj._theString);
PrintLog("theQtObj._theInt=" + theQtObj._theInt);
}
else
PrintLog(" --> theQtObj not defined");
}
var x=0;
function PrintLog(txt)
{
var myBox = document.getElementById("textBoxLog");
myBox.value += ("\n" + x + ": " + txt);
myBox.scrollTop = myBox.scrollHeight;
return (x++);
}
</script>
<p>Test Example to call Qt function from Javascript </p>
<p>
<input type="button" value=" A:init " onclick="buttonA()">
<input type="button" value=" B:get int " onclick="buttonB()">
<input type="button" value="C:get members" onclick="buttonC()">
</p>
<p>
<textarea id="textBoxLog" rows="31" cols="95">
textBoxLog
</textarea>
</p>
</body>
</html>
.cpp file
#include <QWebEngineView>
#include <QApplication>
#include <QtWebChannel/QtWebChannel>
/////////////////////////////////////////////////////////////////
class hfbDisplayWidget : public QWebEngineView
{
Q_OBJECT
public:
hfbDisplayWidget(QWidget * parent) : QWebEngineView(parent)
{
page()->setWebChannel(&_webchannel);
_webchannel.registerObject(QString("theQtObj"), this);
_theString = "Hello World";
_theInt = 777;
}
QWebChannel _webchannel;
Q_INVOKABLE QString _theString;
Q_INVOKABLE int _theInt;
Q_INVOKABLE int getInt(QVariant n)
{
int N = n.toInt();
QString js = QString("PrintLog('c++ getInt(%1) fired!')").arg(N);
_theString = js;
_theInt = N;
page()->runJavaScript(js, [=](const QVariant &rslt){
qDebug() << js << " -> " << rslt;
});
return N*100;
}
Q_INVOKABLE double getDouble(QVariant d)
{
double D = d.toDouble();
QString js = QString("PrintLog('c++ getDouble(%1) fired!')").arg(D);
page()->runJavaScript(js, [=](const QVariant &rslt){
qDebug() << js << " -> " << rslt;
});
return (D+0.12345);
}
Q_INVOKABLE QString getString(QVariant s)
{
QString S = s.toString();
QString js = QString("PrintLog('c++ getString(%1) fired!')").arg(S);
page()->runJavaScript(js, [=](const QVariant &rslt){
qDebug() << js << " -> " << rslt;
});
return (QString("c++ called with:'") + S + QString("'"));
}
};
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
hfbDisplayWidget view(nullptr);
view.setUrl(QUrl("qrc:/hfbTestWebChannel.html"));
view.show();
return a.exec();
}
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
#include "hfbTestWebChannel.moc"
*EDIT to add all code for completeness
Upvotes: 9
Views: 4591
Reputation: 104
Faced same issue... Reading doc's carefully gave me the answer. The answer is that communication between Qt and JS in Qt5 is asynchronous. You have to provide callback function which will be called after method completed and result value received.
Instead of calling
var n = theQtObj.getInt(X);
PrintLog(" back in js with n="+n);
you can call at as
theQtObj.getInt(X, function(n) {
PrintLog(" back in js with n="+n);
});
Upvotes: 5
Reputation: 2848
I had the same issue recently. After some investigation, I figured out how to make it work. Big thanks to the answer of this question: How to use Qt WebEngine and QWebChannel?
In order to get the return value of a method of your QObject, you need to define a Q_PROPERTY wrapping your QObject method. For example:
class SharedObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE int getIntValue();
Q_PROPERTY(int intValue READ getIntValue)
Q_INVOKABLE QString getStringValue();
Q_PROPERTY(QString stringValue READ getStringValue)
}
And then in your HTML, do this:
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
new QWebChannel(qt.webChannelTransport, function (channel) {
window.sharedObject = channel.objects.sharedObject;
alert("intValue: " + sharedObject.intValue);
alert("stringValue: " + sharedObject.stringValue);
});
});
</script>
You should be able to see intValue and stringValue in your JavaScript code. The important bit is to use Q_PROPERTY I think.
Anyway, this resolves my problem, and I hope it helps for someone else too.
Upvotes: 4