Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined properties and return types when using QWebChannel

Tags:

c++

javascript

qt

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:

  • During javascript construction of the new QWebChannel, there are plenty of prints in Qt console for "Property '?'' of object '?' has no notify signal and is not constant, value updates in HTML will be broken!". What's this about, how do I fix it?
  • All the C++ object properties are undefined when trying to retrieve them from the javascript side..what is the correct way to retrieve the properties over the webchannel?
  • All the return values from the C++ functions comes back as undefined...what is the correct way to receive the C++ returned value in the javascript?

.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

like image 416
jan Avatar asked Nov 14 '15 01:11

jan


1 Answers

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);
});
like image 154
Mark Bowler Avatar answered Oct 26 '22 15:10

Mark Bowler